1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2001 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 */
37 #include <X11/Xproto.h> /* for CARD32 */
38 #include <X11/Xatom.h> /* for XA_INTEGER */
39 #include <X11/Intrinsic.h>
40 #include <X11/StringDefs.h>
42 /* We don't actually use any widget internals, but these are included
43 so that gdb will have debug info for the widgets... */
44 #include <X11/IntrinsicP.h>
45 #include <X11/ShellP.h>
49 # include <X11/Xmu/Error.h>
51 # include <Xmu/Error.h>
61 # include <capplet-widget.h>
64 extern Display *gdk_display;
68 #include "resources.h" /* for parse_time() */
69 #include "visual.h" /* for has_writable_cells() */
70 #include "remote.h" /* for xscreensaver_command() */
73 #include "demo-Gtk-widgets.h"
80 #define countof(x) (sizeof((x))/sizeof((*x)))
84 char *progclass = "XScreenSaver";
87 static Bool crapplet_p = False;
88 static Bool initializing_p;
89 static GtkWidget *toplevel_widget;
92 saver_preferences *a, *b;
95 static void *global_prefs_pair; /* I hate C so much... */
97 char *blurb (void) { return progname; }
99 static char *short_version = 0;
102 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
103 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
104 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
107 static void populate_demo_window (GtkWidget *toplevel,
108 int which, prefs_pair *pair);
109 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
110 static int apply_changes_and_save (GtkWidget *widget);
111 static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair);
112 static void await_xscreensaver (GtkWidget *widget);
115 /* Some random utility functions
119 name_to_widget (GtkWidget *widget, const char *name)
121 return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
125 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
126 Takes a scroller, viewport, or list as an argument.
129 ensure_selected_item_visible (GtkWidget *widget)
131 GtkScrolledWindow *scroller = 0;
133 GtkList *list_widget = 0;
137 GtkWidget *selected = 0;
140 gint parent_h, child_y, child_h, children_h, ignore;
141 double ratio_t, ratio_b;
143 if (GTK_IS_SCROLLED_WINDOW (widget))
145 scroller = GTK_SCROLLED_WINDOW (widget);
146 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
147 list_widget = GTK_LIST (GTK_BIN(vp)->child);
149 else if (GTK_IS_VIEWPORT (widget))
151 vp = GTK_VIEWPORT (widget);
152 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
153 list_widget = GTK_LIST (GTK_BIN(vp)->child);
155 else if (GTK_IS_LIST (widget))
157 list_widget = GTK_LIST (widget);
158 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
159 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
164 slist = list_widget->selection;
165 selected = (slist ? GTK_WIDGET (slist->data) : 0);
169 which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
171 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
172 kids; kids = kids->next)
175 adj = gtk_scrolled_window_get_vadjustment (scroller);
177 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
178 &ignore, &ignore, &ignore, &parent_h, &ignore);
179 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
180 &ignore, &child_y, &ignore, &child_h, &ignore);
181 children_h = nkids * child_h;
183 ratio_t = ((double) child_y) / ((double) children_h);
184 ratio_b = ((double) child_y + child_h) / ((double) children_h);
186 if (adj->upper == 0.0) /* no items in list */
189 if (ratio_t < (adj->value / adj->upper) ||
190 ratio_b > ((adj->value + adj->page_size) / adj->upper))
193 int slop = parent_h * 0.75; /* how much to overshoot by */
195 if (ratio_t < (adj->value / adj->upper))
197 double ratio_w = ((double) parent_h) / ((double) children_h);
198 double ratio_l = (ratio_b - ratio_t);
199 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
202 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
204 target = ratio_t * adj->upper;
208 if (target > adj->upper - adj->page_size)
209 target = adj->upper - adj->page_size;
213 gtk_adjustment_set_value (adj, target);
218 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
220 GtkWidget *shell = GTK_WIDGET (user_data);
221 while (shell->parent)
222 shell = shell->parent;
223 gtk_widget_destroy (GTK_WIDGET (shell));
227 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
229 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
231 restart_menu_cb (widget, user_data);
232 warning_dialog_dismiss_cb (widget, user_data);
236 warning_dialog (GtkWidget *parent, const char *message,
237 Boolean restart_button_p, int center)
239 char *msg = strdup (message);
242 GtkWidget *dialog = gtk_dialog_new ();
243 GtkWidget *label = 0;
245 GtkWidget *cancel = 0;
248 while (parent->parent)
249 parent = parent->parent;
255 char *s = strchr (head, '\n');
258 sprintf (name, "label%d", i++);
264 label = gtk_label_new (head);
266 sprintf (buf, "warning_dialog.%s.font", name);
267 GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style);
268 GTK_WIDGET (label)->style->font =
269 gdk_font_load (get_string_resource (buf, "Dialog.Label.Font"));
272 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
273 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
274 label, TRUE, TRUE, 0);
275 gtk_widget_show (label);
286 label = gtk_label_new ("");
287 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
288 label, TRUE, TRUE, 0);
289 gtk_widget_show (label);
291 label = gtk_hbutton_box_new ();
292 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
293 label, TRUE, TRUE, 0);
295 ok = gtk_button_new_with_label ("OK");
296 gtk_container_add (GTK_CONTAINER (label), ok);
298 if (restart_button_p)
300 cancel = gtk_button_new_with_label ("Cancel");
301 gtk_container_add (GTK_CONTAINER (label), cancel);
304 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
305 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
306 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
307 gtk_widget_show (ok);
309 gtk_widget_show (cancel);
310 gtk_widget_show (label);
311 gtk_widget_show (dialog);
312 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
314 if (restart_button_p)
316 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
317 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
319 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
320 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
325 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
326 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
329 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
330 GTK_WIDGET (parent)->window);
332 gdk_window_show (GTK_WIDGET (dialog)->window);
333 gdk_window_raise (GTK_WIDGET (dialog)->window);
340 run_cmd (GtkWidget *widget, Atom command, int arg)
345 apply_changes_and_save (widget);
346 status = xscreensaver_command (gdk_display, command, arg, False, &err);
351 sprintf (buf, "Error:\n\n%s", err);
353 strcpy (buf, "Unknown error!");
354 warning_dialog (widget, buf, False, 100);
361 run_hack (GtkWidget *widget, int which, Bool report_errors_p)
363 if (which < 0) return;
364 apply_changes_and_save (widget);
366 run_cmd (widget, XA_DEMO, which + 1);
370 xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s);
381 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
383 apply_changes_and_save (GTK_WIDGET (menuitem));
388 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
390 apply_changes_and_save (widget);
396 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
399 warning_dialog (GTK_WIDGET (menuitem),
401 "cut unimplemented\n", False, 1);
406 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
409 warning_dialog (GTK_WIDGET (menuitem),
411 "copy unimplemented\n", False, 1);
416 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
419 warning_dialog (GTK_WIDGET (menuitem),
421 "paste unimplemented\n", False, 1);
426 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
429 char *s = strdup (screensaver_id + 4);
432 s2 = strchr (s, ',');
436 sprintf (buf, "%s\n%s\n\n"
437 "For updates, check http://www.jwz.org/xscreensaver/",
441 warning_dialog (GTK_WIDGET (menuitem), buf, False, 100);
446 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
448 /* prefs_pair *pair = (prefs_pair *) client_data; */
449 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
451 saver_preferences *p = pair->a;
454 if (!p->help_url || !*p->help_url)
456 warning_dialog (GTK_WIDGET (menuitem),
458 "No Help URL has been specified.\n", False, 100);
462 help_command = (char *) malloc (strlen (p->load_url_command) +
463 (strlen (p->help_url) * 2) + 20);
464 strcpy (help_command, "( ");
465 sprintf (help_command + strlen(help_command),
466 p->load_url_command, p->help_url, p->help_url);
467 strcat (help_command, " ) &");
468 system (help_command);
474 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
476 run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
481 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
483 run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
488 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
490 run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
495 restart_menu_cb (GtkWidget *widget, gpointer user_data)
498 run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
500 apply_changes_and_save (GTK_WIDGET (widget));
501 xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL);
503 system ("xscreensaver -nosplash &");
506 await_xscreensaver (GTK_WIDGET (widget));
510 await_xscreensaver (GtkWidget *widget)
514 Display *dpy = gdk_display;
515 /* GtkWidget *dialog = 0;*/
518 while (!rversion && (--countdown > 0))
520 /* Check for the version of the running xscreensaver... */
521 server_xscreensaver_version (dpy, &rversion, 0, 0);
523 /* If it's not there yet, wait a second... */
527 /* if (dialog) gtk_widget_destroy (dialog);*/
536 /* Timed out, no screensaver running. */
539 Bool root_p = (geteuid () == 0);
543 "The xscreensaver daemon did not start up properly.\n"
548 "You are running as root. This usually means that xscreensaver\n"
549 "was unable to contact your X server because access control is\n"
550 "turned on. Try running this command:\n"
552 " xhost +localhost\n"
554 "and then selecting `File / Restart Daemon'.\n"
556 "Note that turning off access control will allow anyone logged\n"
557 "on to this machine to access your screen, which might be\n"
558 "considered a security problem. Please read the xscreensaver\n"
559 "manual and FAQ for more information.\n"
561 "You shouldn't run X as root. Instead, you should log in as a\n"
562 "normal user, and `su' as necessary.");
564 strcat (buf, "Please check your $PATH and permissions.");
566 warning_dialog (widget, buf, False, 1);
571 static int _selected_hack_number = -1;
574 selected_hack_number (GtkWidget *toplevel)
577 GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
578 GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
579 GList *slist = list_widget->selection;
580 GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
581 int which = (selected
582 ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
586 return _selected_hack_number;
592 demo_write_init_file (GtkWidget *widget, saver_preferences *p)
594 if (!write_init_file (p, short_version, False))
598 const char *f = init_file_name();
600 warning_dialog (widget,
601 "Error:\n\nCouldn't determine init file name!\n",
605 char *b = (char *) malloc (strlen(f) + 1024);
606 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
607 warning_dialog (widget, b, False, 100);
616 apply_changes_and_save_1 (GtkWidget *widget)
618 /* prefs_pair *pair = (prefs_pair *) client_data; */
619 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
620 saver_preferences *p = pair->a;
621 GtkList *list_widget =
622 GTK_LIST (name_to_widget (widget, "list"));
623 int which = selected_hack_number (widget);
625 GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
626 GtkToggleButton *enabled =
627 GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled"));
628 GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo"));
630 Bool enabled_p = gtk_toggle_button_get_active (enabled);
631 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
632 const char *command = gtk_entry_get_text (cmd);
637 if (which < 0) return -1;
639 if (maybe_reload_init_file (widget, pair) != 0)
642 /* Sanity-check and canonicalize whatever the user typed into the combo box.
644 if (!strcasecmp (visual, "")) visual = "";
645 else if (!strcasecmp (visual, "any")) visual = "";
646 else if (!strcasecmp (visual, "default")) visual = "Default";
647 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
648 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
649 else if (!strcasecmp (visual, "best")) visual = "Best";
650 else if (!strcasecmp (visual, "mono")) visual = "Mono";
651 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
652 else if (!strcasecmp (visual, "gray")) visual = "Gray";
653 else if (!strcasecmp (visual, "grey")) visual = "Gray";
654 else if (!strcasecmp (visual, "color")) visual = "Color";
655 else if (!strcasecmp (visual, "gl")) visual = "GL";
656 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
657 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
658 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
659 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
660 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
661 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
662 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
663 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
664 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
667 gdk_beep (); /* unparsable */
669 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
672 ensure_selected_item_visible (GTK_WIDGET (list_widget));
674 if (!p->screenhacks[which]->visual)
675 p->screenhacks[which]->visual = strdup ("");
676 if (!p->screenhacks[which]->command)
677 p->screenhacks[which]->command = strdup ("");
679 if (p->screenhacks[which]->enabled_p != enabled_p ||
680 !!strcasecmp (p->screenhacks[which]->visual, visual) ||
681 !!strcasecmp (p->screenhacks[which]->command, command))
683 /* Something was changed -- store results into the struct,
686 free (p->screenhacks[which]->visual);
687 free (p->screenhacks[which]->command);
688 p->screenhacks[which]->visual = strdup (visual);
689 p->screenhacks[which]->command = strdup (command);
690 p->screenhacks[which]->enabled_p = enabled_p;
692 return demo_write_init_file (widget, p);
695 /* No changes made */
699 void prefs_ok_cb (GtkButton *button, gpointer user_data);
702 apply_changes_and_save (GtkWidget *widget)
704 prefs_ok_cb ((GtkButton *) widget, 0);
705 return apply_changes_and_save_1 (widget);
710 run_this_cb (GtkButton *button, gpointer user_data)
712 int which = selected_hack_number (GTK_WIDGET (button));
713 if (which < 0) return;
714 if (0 == apply_changes_and_save (GTK_WIDGET (button)))
715 run_hack (GTK_WIDGET (button), which, True);
720 manual_cb (GtkButton *button, gpointer user_data)
722 /* prefs_pair *pair = (prefs_pair *) client_data; */
723 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
724 saver_preferences *p = pair->a;
725 GtkList *list_widget =
726 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
727 int which = selected_hack_number (GTK_WIDGET (button));
728 char *name, *name2, *cmd, *s;
729 if (which < 0) return;
730 apply_changes_and_save (GTK_WIDGET (button));
731 ensure_selected_item_visible (GTK_WIDGET (list_widget));
733 name = strdup (p->screenhacks[which]->command);
735 while (isspace (*name2)) name2++;
737 while (*s && !isspace (*s)) s++;
739 s = strrchr (name2, '/');
742 cmd = get_string_resource ("manualCommand", "ManualCommand");
745 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
747 sprintf (cmd2 + strlen (cmd2),
749 name2, name2, name2, name2);
750 strcat (cmd2, " ) &");
756 warning_dialog (GTK_WIDGET (button),
757 "Error:\n\nno `manualCommand' resource set.",
766 run_next_cb (GtkButton *button, gpointer user_data)
768 /* prefs_pair *pair = (prefs_pair *) client_data; */
769 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
770 saver_preferences *p = pair->a;
772 GtkList *list_widget =
773 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
774 int which = selected_hack_number (GTK_WIDGET (button));
781 if (which >= p->screenhacks_count)
784 apply_changes_and_save (GTK_WIDGET (button));
785 gtk_list_select_item (GTK_LIST (list_widget), which);
786 ensure_selected_item_visible (GTK_WIDGET (list_widget));
787 populate_demo_window (GTK_WIDGET (button), which, pair);
788 run_hack (GTK_WIDGET (button), which, False);
793 run_prev_cb (GtkButton *button, gpointer user_data)
795 /* prefs_pair *pair = (prefs_pair *) client_data; */
796 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
797 saver_preferences *p = pair->a;
799 GtkList *list_widget =
800 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
801 int which = selected_hack_number (GTK_WIDGET (button));
804 which = p->screenhacks_count - 1;
809 which = p->screenhacks_count - 1;
811 apply_changes_and_save (GTK_WIDGET (button));
812 gtk_list_select_item (GTK_LIST (list_widget), which);
813 ensure_selected_item_visible (GTK_WIDGET (list_widget));
814 populate_demo_window (GTK_WIDGET (button), which, pair);
815 run_hack (GTK_WIDGET (button), which, False);
819 /* Helper for the text fields that contain time specifications:
820 this parses the text, and does error checking.
823 hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
828 value = parse_time ((char *) line, sec_p, True);
829 value *= 1000; /* Time measures in microseconds */
835 "Unparsable time format: \"%s\"\n",
837 warning_dialog (widget, b, False, 100);
846 prefs_ok_cb (GtkButton *button, gpointer user_data)
848 /* prefs_pair *pair = (prefs_pair *) client_data; */
849 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
851 saver_preferences *p = pair->a;
852 saver_preferences *p2 = pair->b;
853 Bool changed = False;
855 # define SECONDS(field, name) \
856 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
857 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
861 # define MINUTES(field, name) \
862 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
863 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
867 # define INTEGER(field, name) do { \
868 char *line = gtk_entry_get_text (\
869 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
870 unsigned int value; \
874 else if (sscanf (line, "%u%c", &value, &c) != 1) \
877 sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \
878 warning_dialog (GTK_WIDGET (button), b, False, 100); \
884 # define CHECKBOX(field, name) \
885 field = gtk_toggle_button_get_active (\
886 GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
888 MINUTES (&p2->timeout, "timeout_text");
889 MINUTES (&p2->cycle, "cycle_text");
890 CHECKBOX (p2->lock_p, "lock_button");
891 MINUTES (&p2->lock_timeout, "lock_text");
893 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
894 MINUTES (&p2->dpms_standby, "dpms_standby_text");
895 MINUTES (&p2->dpms_suspend, "dpms_suspend_text");
896 MINUTES (&p2->dpms_off, "dpms_off_text");
898 CHECKBOX (p2->verbose_p, "verbose_button");
899 CHECKBOX (p2->capture_stderr_p, "capture_button");
900 CHECKBOX (p2->splash_p, "splash_button");
902 CHECKBOX (p2->install_cmap_p, "install_button");
903 CHECKBOX (p2->fade_p, "fade_button");
904 CHECKBOX (p2->unfade_p, "unfade_button");
905 SECONDS (&p2->fade_seconds, "fade_text");
912 # define COPY(field) \
913 if (p->field != p2->field) changed = True; \
921 COPY(dpms_enabled_p);
927 COPY(capture_stderr_p);
930 COPY(install_cmap_p);
936 populate_prefs_page (GTK_WIDGET (button), pair);
940 Display *dpy = gdk_display;
941 sync_server_dpms_settings (dpy, p->dpms_enabled_p,
942 p->dpms_standby / 1000,
943 p->dpms_suspend / 1000,
947 demo_write_init_file (GTK_WIDGET (button), p);
953 prefs_cancel_cb (GtkButton *button, gpointer user_data)
955 /* prefs_pair *pair = (prefs_pair *) client_data; */
956 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
959 populate_prefs_page (GTK_WIDGET (button), pair);
964 pref_changed_cb (GtkButton *button, gpointer user_data)
966 if (! initializing_p)
967 apply_changes_and_save (GTK_WIDGET (button));
972 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
973 gpointer client_data)
975 if (event->type == GDK_2BUTTON_PRESS)
977 GtkList *list = GTK_LIST (name_to_widget (button, "list"));
978 int which = gtk_list_child_position (list, GTK_WIDGET (button));
981 run_hack (GTK_WIDGET (button), which, True);
989 list_select_cb (GtkList *list, GtkWidget *child)
991 /* prefs_pair *pair = (prefs_pair *) client_data; */
992 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
994 int which = gtk_list_child_position (list, GTK_WIDGET (child));
995 apply_changes_and_save (GTK_WIDGET (list));
996 populate_demo_window (GTK_WIDGET (list), which, pair);
1000 list_unselect_cb (GtkList *list, GtkWidget *child)
1002 /* prefs_pair *pair = (prefs_pair *) client_data; */
1003 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
1005 apply_changes_and_save (GTK_WIDGET (list));
1006 populate_demo_window (GTK_WIDGET (list), -1, pair);
1010 static int updating_enabled_cb = 0; /* kludge to make sure that enabled_cb
1011 is only run by user action, not by
1014 /* Called when the checkboxes that are in the left column of the
1015 scrolling list are clicked. This both populates the right pane
1016 (just as clicking on the label (really, listitem) does) and
1017 also syncs this checkbox with the right pane Enabled checkbox.
1020 list_checkbox_cb (GtkWidget *cb, gpointer client_data)
1022 prefs_pair *pair = (prefs_pair *) client_data;
1024 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1025 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1027 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1028 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1029 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1033 GtkToggleButton *enabled =
1034 GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
1036 int which = gtk_list_child_position (list, line);
1038 /* remember previous scroll position of the top of the list */
1039 adj = gtk_scrolled_window_get_vadjustment (scroller);
1040 scroll_top = adj->value;
1042 apply_changes_and_save (GTK_WIDGET (list));
1043 gtk_list_select_item (list, which);
1044 /* ensure_selected_item_visible (GTK_WIDGET (list)); */
1045 populate_demo_window (GTK_WIDGET (list), which, pair);
1047 updating_enabled_cb++;
1048 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
1049 GTK_TOGGLE_BUTTON (cb)->active);
1050 updating_enabled_cb--;
1052 /* restore the previous scroll position of the top of the list.
1053 this is weak, but I don't really know why it's moving... */
1054 gtk_adjustment_set_value (adj, scroll_top);
1058 /* Called when the right pane Enabled checkbox is clicked. This syncs
1059 the corresponding checkbox inside the scrolling list to the state
1063 enabled_cb (GtkWidget *cb, gpointer client_data)
1065 int which = selected_hack_number (cb);
1067 if (updating_enabled_cb) return;
1071 GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
1072 GList *kids = GTK_LIST (list)->children;
1073 GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
1074 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1075 GtkWidget *line_check =
1076 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1078 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1079 GTK_TOGGLE_BUTTON (cb)->active);
1084 /* Populating the various widgets
1088 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
1091 format_time (char *buf, Time time)
1093 int s = time / 1000;
1094 unsigned int h = 0, m = 0;
1105 sprintf (buf, "%u:%02u:%02u", h, m, s);
1109 /* Finds the number of the last hack to run, and makes that item be
1110 selected by default.
1113 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
1115 saver_preferences *p = pair->a;
1118 unsigned long nitems, bytesafter;
1120 Display *dpy = gdk_display;
1124 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1125 XA_SCREENSAVER_STATUS,
1126 0, 3, False, XA_INTEGER,
1127 &type, &format, &nitems, &bytesafter,
1128 (unsigned char **) &data)
1130 && type == XA_INTEGER
1133 which = (int) data[2] - 1;
1135 if (data) free (data);
1140 list = GTK_LIST (name_to_widget (toplevel, "list"));
1141 apply_changes_and_save (toplevel);
1142 if (which < p->screenhacks_count)
1144 gtk_list_select_item (list, which);
1145 ensure_selected_item_visible (GTK_WIDGET (list));
1146 populate_demo_window (toplevel, which, pair);
1153 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1155 saver_preferences *p = pair->a;
1156 GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1157 screenhack **hacks = p->screenhacks;
1160 for (h = hacks; h && *h; h++)
1162 /* A GtkList must contain only GtkListItems, but those can contain
1163 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1164 and a Label. We handle single and double click events on the
1165 line itself, for clicking on the text, but the interior checkbox
1166 also handles its own events.
1169 GtkWidget *line_hbox;
1170 GtkWidget *line_check;
1171 GtkWidget *line_label;
1173 char *pretty_name = (h[0]->name
1174 ? strdup (h[0]->name)
1175 : make_hack_name (h[0]->command));
1177 line = gtk_list_item_new ();
1178 line_hbox = gtk_hbox_new (FALSE, 0);
1179 line_check = gtk_check_button_new ();
1180 line_label = gtk_label_new (pretty_name);
1182 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1183 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1184 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1186 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1188 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1190 gtk_widget_show (line_check);
1191 gtk_widget_show (line_label);
1192 gtk_widget_show (line_hbox);
1193 gtk_widget_show (line);
1197 gtk_container_add (GTK_CONTAINER (list), line);
1198 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1199 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1202 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1203 GTK_SIGNAL_FUNC (list_checkbox_cb),
1207 GTK_WIDGET (GTK_BIN(line)->child)->style =
1208 gtk_style_copy (GTK_WIDGET (text_line)->style);
1210 gtk_widget_show (line);
1213 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1214 GTK_SIGNAL_FUNC (list_select_cb),
1216 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1217 GTK_SIGNAL_FUNC (list_unselect_cb),
1223 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1225 saver_preferences *p = pair->a;
1228 format_time (s, p->timeout);
1229 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1230 format_time (s, p->cycle);
1231 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1232 format_time (s, p->lock_timeout);
1233 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1235 format_time (s, p->dpms_standby);
1236 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_standby_text")),s);
1237 format_time (s, p->dpms_suspend);
1238 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_suspend_text")),s);
1239 format_time (s, p->dpms_off);
1240 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_off_text")), s);
1242 format_time (s, p->fade_seconds);
1243 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1245 gtk_toggle_button_set_active (
1246 GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1248 gtk_toggle_button_set_active (
1249 GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1251 gtk_toggle_button_set_active (
1252 GTK_TOGGLE_BUTTON (name_to_widget (top, "capture_button")),
1253 p->capture_stderr_p);
1255 gtk_toggle_button_set_active (
1256 GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
1259 gtk_toggle_button_set_active (
1260 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1262 gtk_toggle_button_set_active (
1263 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1265 gtk_toggle_button_set_active (
1266 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1271 Bool found_any_writable_cells = False;
1272 Bool dpms_supported = False;
1274 Display *dpy = gdk_display;
1275 int nscreens = ScreenCount(dpy);
1277 for (i = 0; i < nscreens; i++)
1279 Screen *s = ScreenOfDisplay (dpy, i);
1280 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1282 found_any_writable_cells = True;
1287 #ifdef HAVE_DPMS_EXTENSION
1289 int op = 0, event = 0, error = 0;
1290 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1291 dpms_supported = True;
1293 #endif /* HAVE_DPMS_EXTENSION */
1296 /* Blanking and Locking
1298 gtk_widget_set_sensitive (
1299 GTK_WIDGET (name_to_widget (top, "lock_label")),
1301 gtk_widget_set_sensitive (
1302 GTK_WIDGET (name_to_widget (top, "lock_text")),
1307 gtk_widget_set_sensitive (
1308 GTK_WIDGET (name_to_widget (top, "dpms_frame")),
1310 gtk_widget_set_sensitive (
1311 GTK_WIDGET (name_to_widget (top, "dpms_button")),
1313 gtk_widget_set_sensitive (
1314 GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
1315 dpms_supported && p->dpms_enabled_p);
1316 gtk_widget_set_sensitive (
1317 GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
1318 dpms_supported && p->dpms_enabled_p);
1319 gtk_widget_set_sensitive (
1320 GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
1321 dpms_supported && p->dpms_enabled_p);
1322 gtk_widget_set_sensitive (
1323 GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
1324 dpms_supported && p->dpms_enabled_p);
1325 gtk_widget_set_sensitive (
1326 GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
1327 dpms_supported && p->dpms_enabled_p);
1328 gtk_widget_set_sensitive (
1329 GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
1330 dpms_supported && p->dpms_enabled_p);
1334 gtk_widget_set_sensitive (
1335 GTK_WIDGET (name_to_widget (top, "cmap_frame")),
1336 found_any_writable_cells);
1337 gtk_widget_set_sensitive (
1338 GTK_WIDGET (name_to_widget (top, "install_button")),
1339 found_any_writable_cells);
1340 gtk_widget_set_sensitive (
1341 GTK_WIDGET (name_to_widget (top, "fade_button")),
1342 found_any_writable_cells);
1343 gtk_widget_set_sensitive (
1344 GTK_WIDGET (name_to_widget (top, "unfade_button")),
1345 found_any_writable_cells);
1347 gtk_widget_set_sensitive (
1348 GTK_WIDGET (name_to_widget (top, "fade_label")),
1349 (found_any_writable_cells &&
1350 (p->fade_p || p->unfade_p)));
1351 gtk_widget_set_sensitive (
1352 GTK_WIDGET (name_to_widget (top, "fade_text")),
1353 (found_any_writable_cells &&
1354 (p->fade_p || p->unfade_p)));
1361 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1363 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1364 "visual", "visual_combo",
1367 for (i = 0; i < countof(names); i++)
1369 GtkWidget *w = name_to_widget (toplevel, names[i]);
1370 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1373 /* I don't know how to handle these yet... */
1375 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1376 for (i = 0; i < countof(names2); i++)
1378 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1379 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1385 /* Even though we've given these text fields a maximum number of characters,
1386 their default size is still about 30 characters wide -- so measure out
1387 a string in their font, and resize them to just fit that.
1390 fix_text_entry_sizes (GtkWidget *toplevel)
1392 const char *names[] = { "timeout_text", "cycle_text", "lock_text",
1393 "dpms_standby_text", "dpms_suspend_text",
1394 "dpms_off_text", "fade_text" };
1399 for (i = 0; i < countof(names); i++)
1401 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1403 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1404 gtk_widget_set_usize (w, width, -2);
1407 /* Now fix the size of the combo box.
1409 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1410 w = GTK_COMBO (w)->entry;
1411 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1412 gtk_widget_set_usize (w, width, -2);
1415 /* Now fix the size of the list.
1417 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1418 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1419 gtk_widget_set_usize (w, width, -2);
1426 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1429 static char *up_arrow_xpm[] = {
1452 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1453 the end of the array (Gtk 1.2.5.) */
1454 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1455 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1458 static char *down_arrow_xpm[] = {
1481 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1482 the end of the array (Gtk 1.2.5.) */
1483 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1484 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1488 pixmapify_button (GtkWidget *toplevel, int down_p)
1492 GtkWidget *pixmapwid;
1496 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1497 (down_p ? "next" : "prev")));
1498 style = gtk_widget_get_style (w);
1500 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1501 &style->bg[GTK_STATE_NORMAL],
1503 ? (gchar **) down_arrow_xpm
1504 : (gchar **) up_arrow_xpm));
1505 pixmapwid = gtk_pixmap_new (pixmap, mask);
1506 gtk_widget_show (pixmapwid);
1507 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1508 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1512 map_next_button_cb (GtkWidget *w, gpointer user_data)
1514 pixmapify_button (w, 1);
1518 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1520 pixmapify_button (w, 0);
1525 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1529 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1530 GtkAllocation *allocation,
1534 GtkWidgetAuxInfo *aux_info;
1536 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1538 aux_info->width = allocation->width;
1539 aux_info->height = -2;
1543 gtk_widget_size_request (label, &req);
1547 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1550 eschew_gtk_lossage (GtkWidget *toplevel)
1552 GtkWidgetAuxInfo *aux_info;
1553 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1555 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1556 aux_info->width = label->allocation.width;
1557 aux_info->height = -2;
1561 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1563 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1564 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1566 gtk_widget_queue_resize (label);
1571 get_hack_blurb (screenhack *hack)
1574 char *prog_name = strdup (hack->command);
1575 char *pretty_name = (hack->name
1576 ? strdup (hack->name)
1577 : make_hack_name (hack->command));
1578 char doc_name[255], doc_class[255];
1581 for (s = prog_name; *s && !isspace(*s); s++)
1584 s = strrchr (prog_name, '/');
1585 if (s) strcpy (prog_name, s+1);
1587 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1588 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1592 doc_string = get_string_resource (doc_name, doc_class);
1595 for (s = doc_string; *s; s++)
1599 /* skip over whitespace at beginning of line */
1601 while (*s && (*s == ' ' || *s == '\t'))
1604 else if (*s == ' ' || *s == '\t')
1606 /* compress all other horizontal whitespace. */
1609 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1611 if (s2 > s) strcpy (s, s2);
1616 while (*s && isspace (*s)) /* Strip trailing whitespace */
1619 /* Delete whitespace at end of each line. */
1620 for (; s > doc_string; s--)
1621 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1624 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1628 if (s2 < s) strcpy (s2, s);
1632 /* Delete leading blank lines. */
1633 for (s = doc_string; *s == '\n'; s++)
1635 if (s > doc_string) strcpy (doc_string, s);
1639 static int doc_installed = 0;
1640 if (doc_installed == 0)
1642 if (get_boolean_resource ("hacks.documentation.isInstalled",
1643 "hacks.documentation.isInstalled"))
1649 if (doc_installed < 0)
1651 strdup ("Error:\n\n"
1652 "The documentation strings do not appear to be "
1653 "installed. This is probably because there is "
1654 "an \"XScreenSaver\" app-defaults file installed "
1655 "that is from an older version of the program. "
1656 "To fix this problem, delete that file, or "
1657 "install a current version (either will work.)");
1659 doc_string = strdup ("");
1667 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1669 saver_preferences *p = pair->a;
1670 screenhack *hack = (which >= 0 && which < p->screenhacks_count
1671 ? p->screenhacks[which] : 0);
1672 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1673 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1674 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1675 GtkToggleButton *enabled =
1676 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1677 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1679 char *pretty_name = (hack
1681 ? strdup (hack->name)
1682 : make_hack_name (hack->command))
1684 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1686 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1687 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1688 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1689 gtk_entry_set_position (cmd, 0);
1691 updating_enabled_cb++;
1692 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1693 updating_enabled_cb--;
1695 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1697 ? (hack->visual && *hack->visual
1702 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1704 sensitize_demo_widgets (toplevel, (hack ? True : False));
1706 if (pretty_name) free (pretty_name);
1707 if (doc_string) free (doc_string);
1709 _selected_hack_number = which;
1714 widget_deleter (GtkWidget *widget, gpointer data)
1716 /* #### Well, I want to destroy these widgets, but if I do that, they get
1717 referenced again, and eventually I get a SEGV. So instead of
1718 destroying them, I'll just hide them, and leak a bunch of memory
1719 every time the disk file changes. Go go go Gtk!
1721 #### Ok, that's a lie, I get a crash even if I just hide the widget
1722 and don't ever delete it. Fuck!
1725 gtk_widget_destroy (widget);
1727 gtk_widget_hide (widget);
1733 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1736 saver_preferences *p = pair->a;
1738 static Bool reentrant_lock = False;
1739 if (reentrant_lock) return 0;
1740 reentrant_lock = True;
1742 if (init_file_changed_p (p))
1744 const char *f = init_file_name();
1749 if (!f || !*f) return 0;
1750 b = (char *) malloc (strlen(f) + 1024);
1753 "file \"%s\" has changed, reloading.\n",
1755 warning_dialog (widget, b, False, 100);
1760 which = selected_hack_number (widget);
1761 list = GTK_LIST (name_to_widget (widget, "list"));
1762 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1763 populate_hack_list (widget, pair);
1764 gtk_list_select_item (list, which);
1765 populate_prefs_page (widget, pair);
1766 populate_demo_window (widget, which, pair);
1767 ensure_selected_item_visible (GTK_WIDGET (list));
1772 reentrant_lock = False;
1778 /* Setting window manager icon
1784 init_icon (GdkWindow *window)
1786 GdkBitmap *mask = 0;
1789 gdk_pixmap_create_from_xpm_d (window, &mask, &transp, logo_xpm);
1791 gdk_window_set_icon (window, 0, pixmap, mask);
1795 /* The main demo-mode command loop.
1800 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1801 XrmRepresentation *type, XrmValue *value, XPointer closure)
1804 for (i = 0; quarks[i]; i++)
1806 if (bindings[i] == XrmBindTightly)
1807 fprintf (stderr, (i == 0 ? "" : "."));
1808 else if (bindings[i] == XrmBindLoosely)
1809 fprintf (stderr, "*");
1811 fprintf (stderr, " ??? ");
1812 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1815 fprintf (stderr, ": %s\n", (char *) value->addr);
1823 the_network_is_not_the_computer (GtkWidget *parent)
1825 Display *dpy = gdk_display;
1826 char *rversion, *ruser, *rhost;
1827 char *luser, *lhost;
1829 struct passwd *p = getpwuid (getuid ());
1830 const char *d = DisplayString (dpy);
1832 # if defined(HAVE_UNAME)
1834 if (uname (&uts) < 0)
1835 lhost = "<UNKNOWN>";
1837 lhost = uts.nodename;
1839 strcpy (lhost, getenv("SYS$NODE"));
1840 # else /* !HAVE_UNAME && !VMS */
1841 strcat (lhost, "<UNKNOWN>");
1842 # endif /* !HAVE_UNAME && !VMS */
1844 if (p && p->pw_name)
1849 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1851 /* Make a buffer that's big enough for a number of copies of all the
1852 strings, plus some. */
1853 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1854 (ruser ? strlen(ruser) : 0) +
1855 (rhost ? strlen(rhost) : 0) +
1862 if (!rversion || !*rversion)
1866 "The XScreenSaver daemon doesn't seem to be running\n"
1867 "on display \"%s\". Launch it now?",
1870 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1872 /* Warn that the two processes are running as different users.
1876 "%s is running as user \"%s\" on host \"%s\".\n"
1877 "But the xscreensaver managing display \"%s\"\n"
1878 "is running as user \"%s\" on host \"%s\".\n"
1880 "Since they are different users, they won't be reading/writing\n"
1881 "the same ~/.xscreensaver file, so %s isn't\n"
1882 "going to work right.\n"
1884 "You should either re-run %s as \"%s\", or re-run\n"
1885 "xscreensaver as \"%s\".\n"
1887 "Restart the xscreensaver daemon now?\n",
1888 progname, luser, lhost,
1890 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1892 progname, (ruser ? ruser : "???"),
1895 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1897 /* Warn that the two processes are running on different hosts.
1901 "%s is running as user \"%s\" on host \"%s\".\n"
1902 "But the xscreensaver managing display \"%s\"\n"
1903 "is running as user \"%s\" on host \"%s\".\n"
1905 "If those two machines don't share a file system (that is,\n"
1906 "if they don't see the same ~%s/.xscreensaver file) then\n"
1907 "%s won't work right.\n"
1909 "Restart the daemon on \"%s\" as \"%s\" now?\n",
1910 progname, luser, lhost,
1912 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1917 else if (!!strcmp (rversion, short_version))
1919 /* Warn that the version numbers don't match.
1923 "This is %s version %s.\n"
1924 "But the xscreensaver managing display \"%s\"\n"
1925 "is version %s. This could cause problems.\n"
1927 "Restart the xscreensaver daemon now?\n",
1928 progname, short_version,
1935 warning_dialog (parent, msg, True, 1);
1941 /* We use this error handler so that X errors are preceeded by the name
1942 of the program that generated them.
1945 demo_ehandler (Display *dpy, XErrorEvent *error)
1947 fprintf (stderr, "\nX error in %s:\n", progname);
1948 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1951 fprintf (stderr, " (nonfatal.)\n");
1956 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
1957 of the program that generated them; and also that we can ignore one
1958 particular bogus error message that Gdk madly spews.
1961 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
1962 const gchar *message, gpointer user_data)
1964 /* Ignore the message "Got event for unknown window: 0x...".
1965 Apparently some events are coming in for the xscreensaver window
1966 (presumably reply events related to the ClientMessage) and Gdk
1967 feels the need to complain about them. So, just suppress any
1968 messages that look like that one.
1970 if (strstr (message, "unknown window"))
1973 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
1974 (log_level == G_LOG_LEVEL_ERROR ? "error" :
1975 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
1976 log_level == G_LOG_LEVEL_WARNING ? "warning" :
1977 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
1978 log_level == G_LOG_LEVEL_INFO ? "info" :
1979 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
1981 ((!*message || message[strlen(message)-1] != '\n')
1986 static char *defaults[] = {
1987 #include "XScreenSaver_ad.h"
1992 #ifdef HAVE_CRAPPLET
1993 static struct poptOption crapplet_options[] = {
1994 {NULL, '\0', 0, NULL, 0}
1996 #endif /* HAVE_CRAPPLET */
2000 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
2005 map_window_cb (GtkWidget *w, gpointer user_data)
2007 Boolean oi = initializing_p;
2008 initializing_p = True;
2009 eschew_gtk_lossage (w);
2010 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
2011 initializing_p = oi;
2016 main (int argc, char **argv)
2019 prefs_pair Pair, *pair;
2020 saver_preferences P, P2, *p, *p2;
2024 Widget toplevel_shell;
2025 GtkWidget *gtk_window;
2026 char *real_progname = argv[0];
2029 initializing_p = True;
2031 s = strrchr (real_progname, '/');
2032 if (s) real_progname = s+1;
2039 memset (p, 0, sizeof (*p));
2040 memset (p2, 0, sizeof (*p2));
2042 global_prefs_pair = pair; /* I hate C so much... */
2044 progname = real_progname;
2046 short_version = (char *) malloc (5);
2047 memcpy (short_version, screensaver_id + 17, 4);
2048 short_version [4] = 0;
2051 /* Register our error message logger for every ``log domain'' known.
2052 There's no way to do this globally, so I grepped the Gtk/Gdk sources
2053 for all of the domains that seem to be in use.
2056 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
2057 "GThread", "Gnome", "GnomeUI", 0 };
2058 for (i = 0; domains[i]; i++)
2059 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
2062 /* This is gross, but Gtk understands --display and not -display...
2064 for (i = 1; i < argc; i++)
2065 if (argv[i][0] && argv[i][1] &&
2066 !strncmp(argv[i], "-display", strlen(argv[i])))
2067 argv[i] = "--display";
2070 /* We need to parse this arg really early... Sigh. */
2071 for (i = 1; i < argc; i++)
2073 (!strcmp(argv[i], "--crapplet") ||
2074 !strcmp(argv[i], "--capplet")))
2076 # ifdef HAVE_CRAPPLET
2079 for (j = i; j < argc; j++) /* remove it from the list */
2080 argv[j] = argv[j+1];
2083 # else /* !HAVE_CRAPPLET */
2084 fprintf (stderr, "%s: not compiled with --crapplet support\n",
2088 # endif /* !HAVE_CRAPPLET */
2091 /* Let Gtk open the X connection, then initialize Xt to use that
2092 same connection. Doctor Frankenstein would be proud.
2094 # ifdef HAVE_CRAPPLET
2097 GnomeClient *client;
2098 GnomeClientFlags flags = 0;
2100 int init_results = gnome_capplet_init ("screensaver-properties",
2102 argc, argv, NULL, 0, NULL);
2104 0 upon successful initialization;
2105 1 if --init-session-settings was passed on the cmdline;
2106 2 if --ignore was passed on the cmdline;
2109 So the 1 signifies just to init the settings, and quit, basically.
2110 (Meaning launch the xscreensaver daemon.)
2113 if (init_results < 0)
2116 g_error ("An initialization error occurred while "
2117 "starting xscreensaver-capplet.\n");
2119 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
2120 real_progname, init_results);
2125 client = gnome_master_client ();
2128 flags = gnome_client_get_flags (client);
2130 if (flags & GNOME_CLIENT_IS_CONNECTED)
2133 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2134 gnome_client_get_id (client));
2137 char *session_args[20];
2139 session_args[i++] = real_progname;
2140 session_args[i++] = "--capplet";
2141 session_args[i++] = "--init-session-settings";
2142 session_args[i] = 0;
2143 gnome_client_set_priority (client, 20);
2144 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2145 gnome_client_set_restart_command (client, i, session_args);
2149 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2152 gnome_client_flush (client);
2155 if (init_results == 1)
2157 system ("xscreensaver -nosplash &");
2163 # endif /* HAVE_CRAPPLET */
2165 gtk_init (&argc, &argv);
2169 /* We must read exactly the same resources as xscreensaver.
2170 That means we must have both the same progclass *and* progname,
2171 at least as far as the resource database is concerned. So,
2172 put "xscreensaver" in argv[0] while initializing Xt.
2174 argv[0] = "xscreensaver";
2178 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2180 XtToolkitInitialize ();
2181 app = XtCreateApplicationContext ();
2183 XtAppSetFallbackResources (app, defaults);
2184 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2185 toplevel_shell = XtAppCreateShell (progname, progclass,
2186 applicationShellWidgetClass,
2189 dpy = XtDisplay (toplevel_shell);
2190 db = XtDatabase (dpy);
2191 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2192 XSetErrorHandler (demo_ehandler);
2195 /* After doing Xt-style command-line processing, complain about any
2196 unrecognized command-line arguments.
2198 for (i = 1; i < argc; i++)
2201 if (s[0] == '-' && s[1] == '-')
2203 if (!strcmp (s, "-prefs"))
2205 else if (crapplet_p)
2206 /* There are lots of random args that we don't care about when we're
2207 started as a crapplet, so just ignore unknown args in that case. */
2211 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2217 /* Load the init file, which may end up consulting the X resource database
2218 and the site-wide app-defaults file. Note that at this point, it's
2219 important that `progname' be "xscreensaver", rather than whatever
2226 /* Now that Xt has been initialized, and the resources have been read,
2227 we can set our `progname' variable to something more in line with
2230 progname = real_progname;
2234 /* Print out all the resources we read. */
2236 XrmName name = { 0 };
2237 XrmClass class = { 0 };
2239 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2245 /* Intern the atoms that xscreensaver_command() needs.
2247 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2248 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2249 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2250 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2251 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2252 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2253 XA_SELECT = XInternAtom (dpy, "SELECT", False);
2254 XA_DEMO = XInternAtom (dpy, "DEMO", False);
2255 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2256 XA_BLANK = XInternAtom (dpy, "BLANK", False);
2257 XA_LOCK = XInternAtom (dpy, "LOCK", False);
2258 XA_EXIT = XInternAtom (dpy, "EXIT", False);
2259 XA_RESTART = XInternAtom (dpy, "RESTART", False);
2262 /* Create the window and all its widgets.
2264 gtk_window = create_xscreensaver_demo ();
2265 toplevel_widget = gtk_window;
2267 /* Set the window's title. */
2270 char *v = (char *) strdup(strchr(screensaver_id, ' '));
2271 char *s1, *s2, *s3, *s4;
2272 s1 = (char *) strchr(v, ' '); s1++;
2273 s2 = (char *) strchr(s1, ' ');
2274 s3 = (char *) strchr(v, '('); s3++;
2275 s4 = (char *) strchr(s3, ')');
2278 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2279 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2283 /* Various other widget initializations...
2285 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2286 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2288 populate_hack_list (gtk_window, pair);
2289 populate_prefs_page (gtk_window, pair);
2290 sensitize_demo_widgets (gtk_window, False);
2291 fix_text_entry_sizes (gtk_window);
2292 scroll_to_current_hack (gtk_window, pair);
2294 gtk_signal_connect (
2295 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2296 "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2297 gtk_signal_connect (
2298 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2299 "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2300 gtk_signal_connect (
2301 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2302 "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2305 /* Handle the -prefs command-line argument. */
2308 GtkNotebook *notebook =
2309 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2310 gtk_notebook_set_page (notebook, 1);
2313 # ifdef HAVE_CRAPPLET
2317 GtkWidget *top_vbox;
2319 capplet = capplet_widget_new ();
2321 top_vbox = GTK_BIN (gtk_window)->child;
2323 gtk_widget_ref (top_vbox);
2324 gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2325 GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2327 /* In crapplet-mode, take off the menubar. */
2328 gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2330 gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2331 gtk_widget_show (capplet);
2332 gtk_widget_hide (gtk_window);
2334 /* Hook up the Control Center's redundant Help button, too. */
2335 gtk_signal_connect (GTK_OBJECT (capplet), "help",
2336 GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2338 /* Issue any warnings about the running xscreensaver daemon. */
2339 the_network_is_not_the_computer (top_vbox);
2342 # endif /* HAVE_CRAPPLET */
2344 gtk_widget_show (gtk_window);
2345 init_icon (GTK_WIDGET(gtk_window)->window);
2347 /* Issue any warnings about the running xscreensaver daemon. */
2348 the_network_is_not_the_computer (gtk_window);
2351 /* Run the Gtk event loop, and not the Xt event loop. This means that
2352 if there were Xt timers or fds registered, they would never get serviced,
2353 and if there were any Xt widgets, they would never have events delivered.
2354 Fortunately, we're using Gtk for all of the UI, and only initialized
2355 Xt so that we could process the command line and use the X resource
2358 initializing_p = False;
2360 # ifdef HAVE_CRAPPLET
2362 capplet_gtk_main ();
2364 # endif /* HAVE_CRAPPLET */
2370 #endif /* HAVE_GTK -- whole file */