1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-1999 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 SECONDS (&p2->fade_seconds, "fade_text");
891 INTEGER (&p2->fade_ticks, "ticks_text");
892 MINUTES (&p2->lock_timeout, "lock_text");
893 SECONDS (&p2->passwd_timeout, "pass_text");
894 CHECKBOX (p2->verbose_p, "verbose_button");
895 CHECKBOX (p2->install_cmap_p, "install_button");
896 CHECKBOX (p2->fade_p, "fade_button");
897 CHECKBOX (p2->unfade_p, "unfade_button");
898 CHECKBOX (p2->lock_p, "lock_button");
905 # define COPY(field) \
906 if (p->field != p2->field) changed = True; \
912 COPY(passwd_timeout);
916 COPY(install_cmap_p);
922 populate_prefs_page (GTK_WIDGET (button), pair);
925 demo_write_init_file (GTK_WIDGET (button), p);
930 prefs_cancel_cb (GtkButton *button, gpointer user_data)
932 /* prefs_pair *pair = (prefs_pair *) client_data; */
933 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
936 populate_prefs_page (GTK_WIDGET (button), pair);
941 pref_changed_cb (GtkButton *button, gpointer user_data)
943 if (! initializing_p)
944 apply_changes_and_save (GTK_WIDGET (button));
949 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
950 gpointer client_data)
952 if (event->type == GDK_2BUTTON_PRESS)
954 GtkList *list = GTK_LIST (name_to_widget (button, "list"));
955 int which = gtk_list_child_position (list, GTK_WIDGET (button));
958 run_hack (GTK_WIDGET (button), which, True);
966 list_select_cb (GtkList *list, GtkWidget *child)
968 /* prefs_pair *pair = (prefs_pair *) client_data; */
969 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
971 int which = gtk_list_child_position (list, GTK_WIDGET (child));
972 apply_changes_and_save (GTK_WIDGET (list));
973 populate_demo_window (GTK_WIDGET (list), which, pair);
977 list_unselect_cb (GtkList *list, GtkWidget *child)
979 /* prefs_pair *pair = (prefs_pair *) client_data; */
980 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
982 apply_changes_and_save (GTK_WIDGET (list));
983 populate_demo_window (GTK_WIDGET (list), -1, pair);
987 static int updating_enabled_cb = 0; /* kludge to make sure that enabled_cb
988 is only run by user action, not by
991 /* Called when the checkboxes that are in the left column of the
992 scrolling list are clicked. This both populates the right pane
993 (just as clicking on the label (really, listitem) does) and
994 also syncs this checkbox with the right pane Enabled checkbox.
997 list_checkbox_cb (GtkWidget *cb, gpointer client_data)
999 prefs_pair *pair = (prefs_pair *) client_data;
1001 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1002 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1004 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1005 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1006 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1010 GtkToggleButton *enabled =
1011 GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
1013 int which = gtk_list_child_position (list, line);
1015 /* remember previous scroll position of the top of the list */
1016 adj = gtk_scrolled_window_get_vadjustment (scroller);
1017 scroll_top = adj->value;
1019 apply_changes_and_save (GTK_WIDGET (list));
1020 gtk_list_select_item (list, which);
1021 /* ensure_selected_item_visible (GTK_WIDGET (list)); */
1022 populate_demo_window (GTK_WIDGET (list), which, pair);
1024 updating_enabled_cb++;
1025 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
1026 GTK_TOGGLE_BUTTON (cb)->active);
1027 updating_enabled_cb--;
1029 /* restore the previous scroll position of the top of the list.
1030 this is weak, but I don't really know why it's moving... */
1031 gtk_adjustment_set_value (adj, scroll_top);
1035 /* Called when the right pane Enabled checkbox is clicked. This syncs
1036 the corresponding checkbox inside the scrolling list to the state
1040 enabled_cb (GtkWidget *cb, gpointer client_data)
1042 int which = selected_hack_number (cb);
1044 if (updating_enabled_cb) return;
1048 GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
1049 GList *kids = GTK_LIST (list)->children;
1050 GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
1051 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1052 GtkWidget *line_check =
1053 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1055 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1056 GTK_TOGGLE_BUTTON (cb)->active);
1061 /* Populating the various widgets
1065 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
1068 format_time (char *buf, Time time)
1070 int s = time / 1000;
1071 unsigned int h = 0, m = 0;
1082 sprintf (buf, "%u:%02u:%02u", h, m, s);
1087 make_pretty_name (const char *shell_command)
1089 char *s = strdup (shell_command);
1093 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1100 s2 = strrchr (s, '/'); /* if pathname, take last component */
1108 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1111 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1112 s2 = get_string_resource (res_name, res_name);
1116 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1117 if (*s2 >= 'A' && *s2 <= 'Z')
1120 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1122 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1128 /* Finds the number of the last hack to run, and makes that item be
1129 selected by default.
1132 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
1134 saver_preferences *p = pair->a;
1137 unsigned long nitems, bytesafter;
1139 Display *dpy = gdk_display;
1143 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1144 XA_SCREENSAVER_STATUS,
1145 0, 3, False, XA_INTEGER,
1146 &type, &format, &nitems, &bytesafter,
1147 (unsigned char **) &data)
1149 && type == XA_INTEGER
1152 which = (int) data[2] - 1;
1154 if (data) free (data);
1159 list = GTK_LIST (name_to_widget (toplevel, "list"));
1160 apply_changes_and_save (toplevel);
1161 if (which < p->screenhacks_count)
1163 gtk_list_select_item (list, which);
1164 ensure_selected_item_visible (GTK_WIDGET (list));
1165 populate_demo_window (toplevel, which, pair);
1172 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1174 saver_preferences *p = pair->a;
1175 GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1176 screenhack **hacks = p->screenhacks;
1179 for (h = hacks; h && *h; h++)
1181 /* A GtkList must contain only GtkListItems, but those can contain
1182 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1183 and a Label. We handle single and double click events on the
1184 line itself, for clicking on the text, but the interior checkbox
1185 also handles its own events.
1188 GtkWidget *line_hbox;
1189 GtkWidget *line_check;
1190 GtkWidget *line_label;
1192 char *pretty_name = (h[0]->name
1193 ? strdup (h[0]->name)
1194 : make_pretty_name (h[0]->command));
1196 line = gtk_list_item_new ();
1197 line_hbox = gtk_hbox_new (FALSE, 0);
1198 line_check = gtk_check_button_new ();
1199 line_label = gtk_label_new (pretty_name);
1201 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1202 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1203 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1205 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1207 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1209 gtk_widget_show (line_check);
1210 gtk_widget_show (line_label);
1211 gtk_widget_show (line_hbox);
1212 gtk_widget_show (line);
1216 gtk_container_add (GTK_CONTAINER (list), line);
1217 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1218 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1221 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1222 GTK_SIGNAL_FUNC (list_checkbox_cb),
1226 GTK_WIDGET (GTK_BIN(line)->child)->style =
1227 gtk_style_copy (GTK_WIDGET (text_line)->style);
1229 gtk_widget_show (line);
1232 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1233 GTK_SIGNAL_FUNC (list_select_cb),
1235 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1236 GTK_SIGNAL_FUNC (list_unselect_cb),
1242 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1244 saver_preferences *p = pair->a;
1247 format_time (s, p->timeout);
1248 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1249 format_time (s, p->cycle);
1250 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1251 format_time (s, p->lock_timeout);
1252 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1253 format_time (s, p->passwd_timeout);
1254 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "pass_text")), s);
1255 format_time (s, p->fade_seconds);
1256 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1257 sprintf (s, "%u", p->fade_ticks);
1258 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "ticks_text")), s);
1260 gtk_toggle_button_set_active (
1261 GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1263 gtk_toggle_button_set_active (
1264 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1266 gtk_toggle_button_set_active (
1267 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1269 gtk_toggle_button_set_active (
1270 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1272 gtk_toggle_button_set_active (
1273 GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1278 Bool found_any_writable_cells = False;
1279 Display *dpy = gdk_display;
1280 int nscreens = ScreenCount(dpy);
1282 for (i = 0; i < nscreens; i++)
1284 Screen *s = ScreenOfDisplay (dpy, i);
1285 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1287 found_any_writable_cells = True;
1292 gtk_widget_set_sensitive (
1293 GTK_WIDGET (name_to_widget (top, "fade_label")),
1294 found_any_writable_cells);
1295 gtk_widget_set_sensitive (
1296 GTK_WIDGET (name_to_widget (top, "ticks_label")),
1297 found_any_writable_cells);
1298 gtk_widget_set_sensitive (
1299 GTK_WIDGET (name_to_widget (top, "fade_text")),
1300 found_any_writable_cells);
1301 gtk_widget_set_sensitive (
1302 GTK_WIDGET (name_to_widget (top, "ticks_text")),
1303 found_any_writable_cells);
1304 gtk_widget_set_sensitive (
1305 GTK_WIDGET (name_to_widget (top, "install_button")),
1306 found_any_writable_cells);
1307 gtk_widget_set_sensitive (
1308 GTK_WIDGET (name_to_widget (top, "fade_button")),
1309 found_any_writable_cells);
1310 gtk_widget_set_sensitive (
1311 GTK_WIDGET (name_to_widget (top, "unfade_button")),
1312 found_any_writable_cells);
1319 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1321 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1322 "visual", "visual_combo",
1325 for (i = 0; i < countof(names); i++)
1327 GtkWidget *w = name_to_widget (toplevel, names[i]);
1328 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1331 /* I don't know how to handle these yet... */
1333 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1334 for (i = 0; i < countof(names2); i++)
1336 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1337 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1343 /* Even though we've given these text fields a maximum number of characters,
1344 their default size is still about 30 characters wide -- so measure out
1345 a string in their font, and resize them to just fit that.
1348 fix_text_entry_sizes (GtkWidget *toplevel)
1350 const char *names[] = { "timeout_text", "cycle_text", "fade_text",
1351 "ticks_text", "lock_text", "pass_text" };
1356 for (i = 0; i < countof(names); i++)
1358 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1360 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1361 gtk_widget_set_usize (w, width, -2);
1364 /* Now fix the size of the combo box.
1366 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1367 w = GTK_COMBO (w)->entry;
1368 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1369 gtk_widget_set_usize (w, width, -2);
1372 /* Now fix the size of the list.
1374 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1375 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1376 gtk_widget_set_usize (w, width, -2);
1383 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1386 static char *up_arrow_xpm[] = {
1409 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1410 the end of the array (Gtk 1.2.5.) */
1411 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1412 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1415 static char *down_arrow_xpm[] = {
1438 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1439 the end of the array (Gtk 1.2.5.) */
1440 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1441 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1445 pixmapify_button (GtkWidget *toplevel, int down_p)
1449 GtkWidget *pixmapwid;
1453 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1454 (down_p ? "next" : "prev")));
1455 style = gtk_widget_get_style (w);
1457 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1458 &style->bg[GTK_STATE_NORMAL],
1460 ? (gchar **) down_arrow_xpm
1461 : (gchar **) up_arrow_xpm));
1462 pixmapwid = gtk_pixmap_new (pixmap, mask);
1463 gtk_widget_show (pixmapwid);
1464 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1465 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1469 map_next_button_cb (GtkWidget *w, gpointer user_data)
1471 pixmapify_button (w, 1);
1475 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1477 pixmapify_button (w, 0);
1482 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1486 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1487 GtkAllocation *allocation,
1491 GtkWidgetAuxInfo *aux_info;
1493 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1495 aux_info->width = allocation->width;
1496 aux_info->height = -2;
1500 gtk_widget_size_request (label, &req);
1504 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1507 eschew_gtk_lossage (GtkWidget *toplevel)
1509 GtkWidgetAuxInfo *aux_info;
1510 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1512 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1513 aux_info->width = label->allocation.width;
1514 aux_info->height = -2;
1518 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1520 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1521 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1523 gtk_widget_queue_resize (label);
1528 get_hack_blurb (screenhack *hack)
1531 char *prog_name = strdup (hack->command);
1532 char *pretty_name = (hack->name
1533 ? strdup (hack->name)
1534 : make_pretty_name (hack->command));
1535 char doc_name[255], doc_class[255];
1538 for (s = prog_name; *s && !isspace(*s); s++)
1541 s = strrchr (prog_name, '/');
1542 if (s) strcpy (prog_name, s+1);
1544 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1545 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1549 doc_string = get_string_resource (doc_name, doc_class);
1552 for (s = doc_string; *s; s++)
1556 /* skip over whitespace at beginning of line */
1558 while (*s && (*s == ' ' || *s == '\t'))
1561 else if (*s == ' ' || *s == '\t')
1563 /* compress all other horizontal whitespace. */
1566 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1568 if (s2 > s) strcpy (s, s2);
1573 while (*s && isspace (*s)) /* Strip trailing whitespace */
1576 /* Delete whitespace at end of each line. */
1577 for (; s > doc_string; s--)
1578 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1581 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1585 if (s2 < s) strcpy (s2, s);
1589 /* Delete leading blank lines. */
1590 for (s = doc_string; *s == '\n'; s++)
1592 if (s > doc_string) strcpy (doc_string, s);
1596 static int doc_installed = 0;
1597 if (doc_installed == 0)
1599 if (get_boolean_resource ("hacks.documentation.isInstalled",
1600 "hacks.documentation.isInstalled"))
1606 if (doc_installed < 0)
1608 strdup ("Error:\n\n"
1609 "The documentation strings do not appear to be "
1610 "installed. This is probably because there is "
1611 "an \"XScreenSaver\" app-defaults file installed "
1612 "that is from an older version of the program. "
1613 "To fix this problem, delete that file, or "
1614 "install a current version (either will work.)");
1616 doc_string = strdup ("");
1624 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1626 saver_preferences *p = pair->a;
1627 screenhack *hack = (which >= 0 && which < p->screenhacks_count
1628 ? p->screenhacks[which] : 0);
1629 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1630 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1631 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1632 GtkToggleButton *enabled =
1633 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1634 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1636 char *pretty_name = (hack
1638 ? strdup (hack->name)
1639 : make_pretty_name (hack->command))
1641 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1643 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1644 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1645 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1646 gtk_entry_set_position (cmd, 0);
1648 updating_enabled_cb++;
1649 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1650 updating_enabled_cb--;
1652 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1654 ? (hack->visual && *hack->visual
1659 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1661 sensitize_demo_widgets (toplevel, (hack ? True : False));
1663 if (pretty_name) free (pretty_name);
1664 if (doc_string) free (doc_string);
1666 _selected_hack_number = which;
1671 widget_deleter (GtkWidget *widget, gpointer data)
1673 /* #### Well, I want to destroy these widgets, but if I do that, they get
1674 referenced again, and eventually I get a SEGV. So instead of
1675 destroying them, I'll just hide them, and leak a bunch of memory
1676 every time the disk file changes. Go go go Gtk!
1678 #### Ok, that's a lie, I get a crash even if I just hide the widget
1679 and don't ever delete it. Fuck!
1682 gtk_widget_destroy (widget);
1684 gtk_widget_hide (widget);
1690 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1693 saver_preferences *p = pair->a;
1695 static Bool reentrant_lock = False;
1696 if (reentrant_lock) return 0;
1697 reentrant_lock = True;
1699 if (init_file_changed_p (p))
1701 const char *f = init_file_name();
1706 if (!f || !*f) return 0;
1707 b = (char *) malloc (strlen(f) + 1024);
1710 "file \"%s\" has changed, reloading.\n",
1712 warning_dialog (widget, b, False, 100);
1717 which = selected_hack_number (widget);
1718 list = GTK_LIST (name_to_widget (widget, "list"));
1719 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1720 populate_hack_list (widget, pair);
1721 gtk_list_select_item (list, which);
1722 populate_prefs_page (widget, pair);
1723 populate_demo_window (widget, which, pair);
1724 ensure_selected_item_visible (GTK_WIDGET (list));
1729 reentrant_lock = False;
1735 /* The main demo-mode command loop.
1740 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1741 XrmRepresentation *type, XrmValue *value, XPointer closure)
1744 for (i = 0; quarks[i]; i++)
1746 if (bindings[i] == XrmBindTightly)
1747 fprintf (stderr, (i == 0 ? "" : "."));
1748 else if (bindings[i] == XrmBindLoosely)
1749 fprintf (stderr, "*");
1751 fprintf (stderr, " ??? ");
1752 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1755 fprintf (stderr, ": %s\n", (char *) value->addr);
1763 the_network_is_not_the_computer (GtkWidget *parent)
1765 Display *dpy = gdk_display;
1766 char *rversion, *ruser, *rhost;
1767 char *luser, *lhost;
1769 struct passwd *p = getpwuid (getuid ());
1770 const char *d = DisplayString (dpy);
1772 # if defined(HAVE_UNAME)
1774 if (uname (&uts) < 0)
1775 lhost = "<UNKNOWN>";
1777 lhost = uts.nodename;
1779 strcpy (lhost, getenv("SYS$NODE"));
1780 # else /* !HAVE_UNAME && !VMS */
1781 strcat (lhost, "<UNKNOWN>");
1782 # endif /* !HAVE_UNAME && !VMS */
1784 if (p && p->pw_name)
1789 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1791 /* Make a buffer that's big enough for a number of copies of all the
1792 strings, plus some. */
1793 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1794 (ruser ? strlen(ruser) : 0) +
1795 (rhost ? strlen(rhost) : 0) +
1802 if (!rversion || !*rversion)
1806 "The XScreenSaver daemon doesn't seem to be running\n"
1807 "on display \"%s\". Launch it now?",
1810 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1812 /* Warn that the two processes are running as different users.
1816 "%s is running as user \"%s\" on host \"%s\".\n"
1817 "But the xscreensaver managing display \"%s\"\n"
1818 "is running as user \"%s\" on host \"%s\".\n"
1820 "Since they are different users, they won't be reading/writing\n"
1821 "the same ~/.xscreensaver file, so %s isn't\n"
1822 "going to work right.\n"
1824 "You should either re-run %s as \"%s\", or re-run\n"
1825 "xscreensaver as \"%s\".\n"
1827 "Restart the xscreensaver daemon now?\n",
1828 progname, luser, lhost,
1830 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1832 progname, (ruser ? ruser : "???"),
1835 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1837 /* Warn that the two processes are running on different hosts.
1841 "%s is running as user \"%s\" on host \"%s\".\n"
1842 "But the xscreensaver managing display \"%s\"\n"
1843 "is running as user \"%s\" on host \"%s\".\n"
1845 "If those two machines don't share a file system (that is,\n"
1846 "if they don't see the same ~%s/.xscreensaver file) then\n"
1847 "%s won't work right.\n"
1849 "Restart the daemon on \"%s\" as \"%s\" now?\n",
1850 progname, luser, lhost,
1852 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1857 else if (!!strcmp (rversion, short_version))
1859 /* Warn that the version numbers don't match.
1863 "This is %s version %s.\n"
1864 "But the xscreensaver managing display \"%s\"\n"
1865 "is version %s. This could cause problems.\n"
1867 "Restart the xscreensaver daemon now?\n",
1868 progname, short_version,
1875 warning_dialog (parent, msg, True, 1);
1881 /* We use this error handler so that X errors are preceeded by the name
1882 of the program that generated them.
1885 demo_ehandler (Display *dpy, XErrorEvent *error)
1887 fprintf (stderr, "\nX error in %s:\n", progname);
1888 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1891 fprintf (stderr, " (nonfatal.)\n");
1896 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
1897 of the program that generated them; and also that we can ignore one
1898 particular bogus error message that Gdk madly spews.
1901 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
1902 const gchar *message, gpointer user_data)
1904 /* Ignore the message "Got event for unknown window: 0x...".
1905 Apparently some events are coming in for the xscreensaver window
1906 (presumably reply events related to the ClientMessage) and Gdk
1907 feels the need to complain about them. So, just suppress any
1908 messages that look like that one.
1910 if (strstr (message, "unknown window"))
1913 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
1914 (log_level == G_LOG_LEVEL_ERROR ? "error" :
1915 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
1916 log_level == G_LOG_LEVEL_WARNING ? "warning" :
1917 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
1918 log_level == G_LOG_LEVEL_INFO ? "info" :
1919 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
1921 ((!*message || message[strlen(message)-1] != '\n')
1926 static char *defaults[] = {
1927 #include "XScreenSaver_ad.h"
1932 #ifdef HAVE_CRAPPLET
1933 static struct poptOption crapplet_options[] = {
1934 {NULL, '\0', 0, NULL, 0}
1936 #endif /* HAVE_CRAPPLET */
1940 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
1945 map_window_cb (GtkWidget *w, gpointer user_data)
1947 Boolean oi = initializing_p;
1948 initializing_p = True;
1949 eschew_gtk_lossage (w);
1950 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
1951 initializing_p = oi;
1956 main (int argc, char **argv)
1959 prefs_pair Pair, *pair;
1960 saver_preferences P, P2, *p, *p2;
1964 Widget toplevel_shell;
1965 GtkWidget *gtk_window;
1966 char *real_progname = argv[0];
1969 initializing_p = True;
1971 s = strrchr (real_progname, '/');
1972 if (s) real_progname = s+1;
1979 memset (p, 0, sizeof (*p));
1980 memset (p2, 0, sizeof (*p2));
1982 global_prefs_pair = pair; /* I hate C so much... */
1984 progname = real_progname;
1986 short_version = (char *) malloc (5);
1987 memcpy (short_version, screensaver_id + 17, 4);
1988 short_version [4] = 0;
1991 /* Register our error message logger for every ``log domain'' known.
1992 There's no way to do this globally, so I grepped the Gtk/Gdk sources
1993 for all of the domains that seem to be in use.
1996 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
1997 "GThread", "Gnome", "GnomeUI", 0 };
1998 for (i = 0; domains[i]; i++)
1999 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
2002 /* This is gross, but Gtk understands --display and not -display...
2004 for (i = 1; i < argc; i++)
2005 if (argv[i][0] && argv[i][1] &&
2006 !strncmp(argv[i], "-display", strlen(argv[i])))
2007 argv[i] = "--display";
2010 /* We need to parse this arg really early... Sigh. */
2011 for (i = 1; i < argc; i++)
2013 (!strcmp(argv[i], "--crapplet") ||
2014 !strcmp(argv[i], "--capplet")))
2016 # ifdef HAVE_CRAPPLET
2019 for (j = i; j < argc; j++) /* remove it from the list */
2020 argv[j] = argv[j+1];
2023 # else /* !HAVE_CRAPPLET */
2024 fprintf (stderr, "%s: not compiled with --crapplet support\n",
2028 # endif /* !HAVE_CRAPPLET */
2031 /* Let Gtk open the X connection, then initialize Xt to use that
2032 same connection. Doctor Frankenstein would be proud.
2034 # ifdef HAVE_CRAPPLET
2037 GnomeClient *client;
2038 GnomeClientFlags flags = 0;
2040 int init_results = gnome_capplet_init ("screensaver-properties",
2042 argc, argv, NULL, 0, NULL);
2044 0 upon successful initialization;
2045 1 if --init-session-settings was passed on the cmdline;
2046 2 if --ignore was passed on the cmdline;
2049 So the 1 signifies just to init the settings, and quit, basically.
2050 (Meaning launch the xscreensaver daemon.)
2053 if (init_results < 0)
2056 g_error ("An initialization error occurred while "
2057 "starting xscreensaver-capplet.\n");
2059 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
2060 real_progname, init_results);
2065 client = gnome_master_client ();
2068 flags = gnome_client_get_flags (client);
2070 if (flags & GNOME_CLIENT_IS_CONNECTED)
2073 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2074 gnome_client_get_id (client));
2077 char *session_args[20];
2079 session_args[i++] = real_progname;
2080 session_args[i++] = "--capplet";
2081 session_args[i++] = "--init-session-settings";
2082 session_args[i] = 0;
2083 gnome_client_set_priority (client, 20);
2084 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2085 gnome_client_set_restart_command (client, i, session_args);
2089 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2092 gnome_client_flush (client);
2095 if (init_results == 1)
2097 system ("xscreensaver -nosplash &");
2103 # endif /* HAVE_CRAPPLET */
2105 gtk_init (&argc, &argv);
2109 /* We must read exactly the same resources as xscreensaver.
2110 That means we must have both the same progclass *and* progname,
2111 at least as far as the resource database is concerned. So,
2112 put "xscreensaver" in argv[0] while initializing Xt.
2114 argv[0] = "xscreensaver";
2118 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2120 XtToolkitInitialize ();
2121 app = XtCreateApplicationContext ();
2123 XtAppSetFallbackResources (app, defaults);
2124 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2125 toplevel_shell = XtAppCreateShell (progname, progclass,
2126 applicationShellWidgetClass,
2129 dpy = XtDisplay (toplevel_shell);
2130 db = XtDatabase (dpy);
2131 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2132 XSetErrorHandler (demo_ehandler);
2135 /* After doing Xt-style command-line processing, complain about any
2136 unrecognized command-line arguments.
2138 for (i = 1; i < argc; i++)
2141 if (s[0] == '-' && s[1] == '-')
2143 if (!strcmp (s, "-prefs"))
2145 else if (crapplet_p)
2146 /* There are lots of random args that we don't care about when we're
2147 started as a crapplet, so just ignore unknown args in that case. */
2151 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2157 /* Load the init file, which may end up consulting the X resource database
2158 and the site-wide app-defaults file. Note that at this point, it's
2159 important that `progname' be "xscreensaver", rather than whatever
2166 /* Now that Xt has been initialized, and the resources have been read,
2167 we can set our `progname' variable to something more in line with
2170 progname = real_progname;
2174 /* Print out all the resources we read. */
2176 XrmName name = { 0 };
2177 XrmClass class = { 0 };
2179 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2185 /* Intern the atoms that xscreensaver_command() needs.
2187 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2188 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2189 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2190 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2191 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2192 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2193 XA_SELECT = XInternAtom (dpy, "SELECT", False);
2194 XA_DEMO = XInternAtom (dpy, "DEMO", False);
2195 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2196 XA_BLANK = XInternAtom (dpy, "BLANK", False);
2197 XA_LOCK = XInternAtom (dpy, "LOCK", False);
2198 XA_EXIT = XInternAtom (dpy, "EXIT", False);
2199 XA_RESTART = XInternAtom (dpy, "RESTART", False);
2202 /* Create the window and all its widgets.
2204 gtk_window = create_xscreensaver_demo ();
2205 toplevel_widget = gtk_window;
2207 /* Set the window's title. */
2210 char *v = (char *) strdup(strchr(screensaver_id, ' '));
2211 char *s1, *s2, *s3, *s4;
2212 s1 = (char *) strchr(v, ' '); s1++;
2213 s2 = (char *) strchr(s1, ' ');
2214 s3 = (char *) strchr(v, '('); s3++;
2215 s4 = (char *) strchr(s3, ')');
2218 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2219 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2223 /* Various other widget initializations...
2225 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2226 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2228 populate_hack_list (gtk_window, pair);
2229 populate_prefs_page (gtk_window, pair);
2230 sensitize_demo_widgets (gtk_window, False);
2231 fix_text_entry_sizes (gtk_window);
2232 scroll_to_current_hack (gtk_window, pair);
2234 gtk_signal_connect (
2235 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2236 "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2237 gtk_signal_connect (
2238 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2239 "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2240 gtk_signal_connect (
2241 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2242 "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2245 /* Handle the -prefs command-line argument. */
2248 GtkNotebook *notebook =
2249 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2250 gtk_notebook_set_page (notebook, 1);
2253 # ifdef HAVE_CRAPPLET
2257 GtkWidget *top_vbox;
2259 capplet = capplet_widget_new ();
2261 top_vbox = GTK_BIN (gtk_window)->child;
2263 gtk_widget_ref (top_vbox);
2264 gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2265 GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2267 /* In crapplet-mode, take off the menubar. */
2268 gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2270 gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2271 gtk_widget_show (capplet);
2272 gtk_widget_hide (gtk_window);
2274 /* Hook up the Control Center's redundant Help button, too. */
2275 gtk_signal_connect (GTK_OBJECT (capplet), "help",
2276 GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2278 /* Issue any warnings about the running xscreensaver daemon. */
2279 the_network_is_not_the_computer (top_vbox);
2282 # endif /* HAVE_CRAPPLET */
2284 gtk_widget_show (gtk_window);
2286 /* Issue any warnings about the running xscreensaver daemon. */
2287 the_network_is_not_the_computer (gtk_window);
2290 /* Run the Gtk event loop, and not the Xt event loop. This means that
2291 if there were Xt timers or fds registered, they would never get serviced,
2292 and if there were any Xt widgets, they would never have events delivered.
2293 Fortunately, we're using Gtk for all of the UI, and only initialized
2294 Xt so that we could process the command line and use the X resource
2297 initializing_p = False;
2299 # ifdef HAVE_CRAPPLET
2301 capplet_gtk_main ();
2303 # endif /* HAVE_CRAPPLET */
2309 #endif /* HAVE_GTK -- whole file */