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);
114 /* Some random utility functions
118 name_to_widget (GtkWidget *widget, const char *name)
120 return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
124 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
125 Takes a scroller, viewport, or list as an argument.
128 ensure_selected_item_visible (GtkWidget *widget)
130 GtkScrolledWindow *scroller = 0;
132 GtkList *list_widget = 0;
136 GtkWidget *selected = 0;
139 gint parent_h, child_y, child_h, children_h, ignore;
140 double ratio_t, ratio_b;
142 if (GTK_IS_SCROLLED_WINDOW (widget))
144 scroller = GTK_SCROLLED_WINDOW (widget);
145 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
146 list_widget = GTK_LIST (GTK_BIN(vp)->child);
148 else if (GTK_IS_VIEWPORT (widget))
150 vp = GTK_VIEWPORT (widget);
151 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
152 list_widget = GTK_LIST (GTK_BIN(vp)->child);
154 else if (GTK_IS_LIST (widget))
156 list_widget = GTK_LIST (widget);
157 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
158 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
163 slist = list_widget->selection;
164 selected = (slist ? GTK_WIDGET (slist->data) : 0);
168 which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
170 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
171 kids; kids = kids->next)
174 adj = gtk_scrolled_window_get_vadjustment (scroller);
176 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
177 &ignore, &ignore, &ignore, &parent_h, &ignore);
178 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
179 &ignore, &child_y, &ignore, &child_h, &ignore);
180 children_h = nkids * child_h;
182 ratio_t = ((double) child_y) / ((double) children_h);
183 ratio_b = ((double) child_y + child_h) / ((double) children_h);
185 if (adj->upper == 0.0) /* no items in list */
188 if (ratio_t < (adj->value / adj->upper) ||
189 ratio_b > ((adj->value + adj->page_size) / adj->upper))
192 int slop = parent_h * 0.75; /* how much to overshoot by */
194 if (ratio_t < (adj->value / adj->upper))
196 double ratio_w = ((double) parent_h) / ((double) children_h);
197 double ratio_l = (ratio_b - ratio_t);
198 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
201 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
203 target = ratio_t * adj->upper;
207 if (target > adj->upper - adj->page_size)
208 target = adj->upper - adj->page_size;
212 gtk_adjustment_set_value (adj, target);
217 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
219 GtkWidget *shell = GTK_WIDGET (user_data);
220 while (shell->parent)
221 shell = shell->parent;
222 gtk_widget_destroy (GTK_WIDGET (shell));
226 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
228 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
230 restart_menu_cb (widget, user_data);
231 warning_dialog_dismiss_cb (widget, user_data);
235 warning_dialog (GtkWidget *parent, const char *message,
236 Boolean restart_button_p, int center)
238 char *msg = strdup (message);
241 GtkWidget *dialog = gtk_dialog_new ();
242 GtkWidget *label = 0;
244 GtkWidget *cancel = 0;
247 while (parent->parent)
248 parent = parent->parent;
254 char *s = strchr (head, '\n');
257 sprintf (name, "label%d", i++);
263 label = gtk_label_new (head);
265 sprintf (buf, "warning_dialog.%s.font", name);
266 GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style);
267 GTK_WIDGET (label)->style->font =
268 gdk_font_load (get_string_resource (buf, "Dialog.Label.Font"));
271 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
272 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
273 label, TRUE, TRUE, 0);
274 gtk_widget_show (label);
285 label = gtk_label_new ("");
286 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
287 label, TRUE, TRUE, 0);
288 gtk_widget_show (label);
290 label = gtk_hbutton_box_new ();
291 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
292 label, TRUE, TRUE, 0);
294 ok = gtk_button_new_with_label ("OK");
295 gtk_container_add (GTK_CONTAINER (label), ok);
297 if (restart_button_p)
299 cancel = gtk_button_new_with_label ("Cancel");
300 gtk_container_add (GTK_CONTAINER (label), cancel);
303 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
304 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
305 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
306 gtk_widget_show (ok);
308 gtk_widget_show (cancel);
309 gtk_widget_show (label);
310 gtk_widget_show (dialog);
311 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
313 if (restart_button_p)
315 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
316 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
318 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
319 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
324 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
325 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
328 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
329 GTK_WIDGET (parent)->window);
331 gdk_window_show (GTK_WIDGET (dialog)->window);
332 gdk_window_raise (GTK_WIDGET (dialog)->window);
339 run_cmd (GtkWidget *widget, Atom command, int arg)
344 apply_changes_and_save (widget);
345 status = xscreensaver_command (gdk_display, command, arg, False, &err);
350 sprintf (buf, "Error:\n\n%s", err);
352 strcpy (buf, "Unknown error!");
353 warning_dialog (widget, buf, False, 100);
360 run_hack (GtkWidget *widget, int which, Bool report_errors_p)
362 if (which < 0) return;
363 apply_changes_and_save (widget);
365 run_cmd (widget, XA_DEMO, which + 1);
369 xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s);
380 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
382 apply_changes_and_save (GTK_WIDGET (menuitem));
387 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
389 apply_changes_and_save (widget);
395 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
398 warning_dialog (GTK_WIDGET (menuitem),
400 "cut unimplemented\n", False, 1);
405 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
408 warning_dialog (GTK_WIDGET (menuitem),
410 "copy unimplemented\n", False, 1);
415 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
418 warning_dialog (GTK_WIDGET (menuitem),
420 "paste unimplemented\n", False, 1);
425 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
428 char *s = strdup (screensaver_id + 4);
431 s2 = strchr (s, ',');
435 sprintf (buf, "%s\n%s\n\n"
436 "For updates, check http://www.jwz.org/xscreensaver/",
440 warning_dialog (GTK_WIDGET (menuitem), buf, False, 100);
445 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
447 /* prefs_pair *pair = (prefs_pair *) client_data; */
448 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
450 saver_preferences *p = pair->a;
453 if (!p->help_url || !*p->help_url)
455 warning_dialog (GTK_WIDGET (menuitem),
457 "No Help URL has been specified.\n", False, 100);
461 help_command = (char *) malloc (strlen (p->load_url_command) +
462 (strlen (p->help_url) * 2) + 20);
463 strcpy (help_command, "( ");
464 sprintf (help_command + strlen(help_command),
465 p->load_url_command, p->help_url, p->help_url);
466 strcat (help_command, " ) &");
467 system (help_command);
473 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
475 run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
480 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
482 run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
487 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
489 run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
494 restart_menu_cb (GtkWidget *widget, gpointer user_data)
497 run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
499 apply_changes_and_save (GTK_WIDGET (widget));
500 xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL);
502 system ("xscreensaver -nosplash &");
507 static int _selected_hack_number = -1;
510 selected_hack_number (GtkWidget *toplevel)
513 GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
514 GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
515 GList *slist = list_widget->selection;
516 GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
517 int which = (selected
518 ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
522 return _selected_hack_number;
528 demo_write_init_file (GtkWidget *widget, saver_preferences *p)
530 if (!write_init_file (p, short_version, False))
534 const char *f = init_file_name();
536 warning_dialog (widget,
537 "Error:\n\nCouldn't determine init file name!\n",
541 char *b = (char *) malloc (strlen(f) + 1024);
542 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
543 warning_dialog (widget, b, False, 100);
552 apply_changes_and_save_1 (GtkWidget *widget)
554 /* prefs_pair *pair = (prefs_pair *) client_data; */
555 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
556 saver_preferences *p = pair->a;
557 GtkList *list_widget =
558 GTK_LIST (name_to_widget (widget, "list"));
559 int which = selected_hack_number (widget);
561 GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
562 GtkToggleButton *enabled =
563 GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled"));
564 GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo"));
566 Bool enabled_p = gtk_toggle_button_get_active (enabled);
567 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
568 const char *command = gtk_entry_get_text (cmd);
573 if (which < 0) return -1;
575 if (maybe_reload_init_file (widget, pair) != 0)
578 /* Sanity-check and canonicalize whatever the user typed into the combo box.
580 if (!strcasecmp (visual, "")) visual = "";
581 else if (!strcasecmp (visual, "any")) visual = "";
582 else if (!strcasecmp (visual, "default")) visual = "Default";
583 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
584 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
585 else if (!strcasecmp (visual, "best")) visual = "Best";
586 else if (!strcasecmp (visual, "mono")) visual = "Mono";
587 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
588 else if (!strcasecmp (visual, "gray")) visual = "Gray";
589 else if (!strcasecmp (visual, "grey")) visual = "Gray";
590 else if (!strcasecmp (visual, "color")) visual = "Color";
591 else if (!strcasecmp (visual, "gl")) visual = "GL";
592 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
593 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
594 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
595 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
596 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
597 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
598 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
599 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
600 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
603 gdk_beep (); /* unparsable */
605 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
608 ensure_selected_item_visible (GTK_WIDGET (list_widget));
610 if (!p->screenhacks[which]->visual)
611 p->screenhacks[which]->visual = strdup ("");
612 if (!p->screenhacks[which]->command)
613 p->screenhacks[which]->command = strdup ("");
615 if (p->screenhacks[which]->enabled_p != enabled_p ||
616 !!strcasecmp (p->screenhacks[which]->visual, visual) ||
617 !!strcasecmp (p->screenhacks[which]->command, command))
619 /* Something was changed -- store results into the struct,
622 free (p->screenhacks[which]->visual);
623 free (p->screenhacks[which]->command);
624 p->screenhacks[which]->visual = strdup (visual);
625 p->screenhacks[which]->command = strdup (command);
626 p->screenhacks[which]->enabled_p = enabled_p;
628 return demo_write_init_file (widget, p);
631 /* No changes made */
635 void prefs_ok_cb (GtkButton *button, gpointer user_data);
638 apply_changes_and_save (GtkWidget *widget)
640 prefs_ok_cb ((GtkButton *) widget, 0);
641 return apply_changes_and_save_1 (widget);
646 run_this_cb (GtkButton *button, gpointer user_data)
648 int which = selected_hack_number (GTK_WIDGET (button));
649 if (which < 0) return;
650 if (0 == apply_changes_and_save (GTK_WIDGET (button)))
651 run_hack (GTK_WIDGET (button), which, True);
656 manual_cb (GtkButton *button, gpointer user_data)
658 /* prefs_pair *pair = (prefs_pair *) client_data; */
659 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
660 saver_preferences *p = pair->a;
661 GtkList *list_widget =
662 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
663 int which = selected_hack_number (GTK_WIDGET (button));
664 char *name, *name2, *cmd, *s;
665 if (which < 0) return;
666 apply_changes_and_save (GTK_WIDGET (button));
667 ensure_selected_item_visible (GTK_WIDGET (list_widget));
669 name = strdup (p->screenhacks[which]->command);
671 while (isspace (*name2)) name2++;
673 while (*s && !isspace (*s)) s++;
675 s = strrchr (name2, '/');
678 cmd = get_string_resource ("manualCommand", "ManualCommand");
681 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
683 sprintf (cmd2 + strlen (cmd2),
685 name2, name2, name2, name2);
686 strcat (cmd2, " ) &");
692 warning_dialog (GTK_WIDGET (button),
693 "Error:\n\nno `manualCommand' resource set.",
702 run_next_cb (GtkButton *button, gpointer user_data)
704 /* prefs_pair *pair = (prefs_pair *) client_data; */
705 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
706 saver_preferences *p = pair->a;
708 GtkList *list_widget =
709 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
710 int which = selected_hack_number (GTK_WIDGET (button));
717 if (which >= p->screenhacks_count)
720 apply_changes_and_save (GTK_WIDGET (button));
721 gtk_list_select_item (GTK_LIST (list_widget), which);
722 ensure_selected_item_visible (GTK_WIDGET (list_widget));
723 populate_demo_window (GTK_WIDGET (button), which, pair);
724 run_hack (GTK_WIDGET (button), which, False);
729 run_prev_cb (GtkButton *button, gpointer user_data)
731 /* prefs_pair *pair = (prefs_pair *) client_data; */
732 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
733 saver_preferences *p = pair->a;
735 GtkList *list_widget =
736 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
737 int which = selected_hack_number (GTK_WIDGET (button));
740 which = p->screenhacks_count - 1;
745 which = p->screenhacks_count - 1;
747 apply_changes_and_save (GTK_WIDGET (button));
748 gtk_list_select_item (GTK_LIST (list_widget), which);
749 ensure_selected_item_visible (GTK_WIDGET (list_widget));
750 populate_demo_window (GTK_WIDGET (button), which, pair);
751 run_hack (GTK_WIDGET (button), which, False);
755 /* Helper for the text fields that contain time specifications:
756 this parses the text, and does error checking.
759 hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
764 value = parse_time ((char *) line, sec_p, True);
765 value *= 1000; /* Time measures in microseconds */
771 "Unparsable time format: \"%s\"\n",
773 warning_dialog (widget, b, False, 100);
782 prefs_ok_cb (GtkButton *button, gpointer user_data)
784 /* prefs_pair *pair = (prefs_pair *) client_data; */
785 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
787 saver_preferences *p = pair->a;
788 saver_preferences *p2 = pair->b;
789 Bool changed = False;
791 # define SECONDS(field, name) \
792 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
793 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
797 # define MINUTES(field, name) \
798 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
799 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
803 # define INTEGER(field, name) do { \
804 char *line = gtk_entry_get_text (\
805 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
806 unsigned int value; \
810 else if (sscanf (line, "%u%c", &value, &c) != 1) \
813 sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \
814 warning_dialog (GTK_WIDGET (button), b, False, 100); \
820 # define CHECKBOX(field, name) \
821 field = gtk_toggle_button_get_active (\
822 GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
824 MINUTES (&p2->timeout, "timeout_text");
825 MINUTES (&p2->cycle, "cycle_text");
826 SECONDS (&p2->fade_seconds, "fade_text");
827 INTEGER (&p2->fade_ticks, "ticks_text");
828 MINUTES (&p2->lock_timeout, "lock_text");
829 SECONDS (&p2->passwd_timeout, "pass_text");
830 CHECKBOX (p2->verbose_p, "verbose_button");
831 CHECKBOX (p2->install_cmap_p, "install_button");
832 CHECKBOX (p2->fade_p, "fade_button");
833 CHECKBOX (p2->unfade_p, "unfade_button");
834 CHECKBOX (p2->lock_p, "lock_button");
841 # define COPY(field) \
842 if (p->field != p2->field) changed = True; \
848 COPY(passwd_timeout);
852 COPY(install_cmap_p);
858 populate_prefs_page (GTK_WIDGET (button), pair);
861 demo_write_init_file (GTK_WIDGET (button), p);
866 prefs_cancel_cb (GtkButton *button, gpointer user_data)
868 /* prefs_pair *pair = (prefs_pair *) client_data; */
869 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
872 populate_prefs_page (GTK_WIDGET (button), pair);
877 pref_changed_cb (GtkButton *button, gpointer user_data)
879 if (! initializing_p)
880 apply_changes_and_save (GTK_WIDGET (button));
885 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
886 gpointer client_data)
888 if (event->type == GDK_2BUTTON_PRESS)
890 GtkList *list = GTK_LIST (name_to_widget (button, "list"));
891 int which = gtk_list_child_position (list, GTK_WIDGET (button));
894 run_hack (GTK_WIDGET (button), which, True);
902 list_select_cb (GtkList *list, GtkWidget *child)
904 /* prefs_pair *pair = (prefs_pair *) client_data; */
905 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
907 int which = gtk_list_child_position (list, GTK_WIDGET (child));
908 apply_changes_and_save (GTK_WIDGET (list));
909 populate_demo_window (GTK_WIDGET (list), which, pair);
913 list_unselect_cb (GtkList *list, GtkWidget *child)
915 /* prefs_pair *pair = (prefs_pair *) client_data; */
916 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
918 apply_changes_and_save (GTK_WIDGET (list));
919 populate_demo_window (GTK_WIDGET (list), -1, pair);
923 static int updating_enabled_cb = 0; /* kludge to make sure that enabled_cb
924 is only run by user action, not by
927 /* Called when the checkboxes that are in the left column of the
928 scrolling list are clicked. This both populates the right pane
929 (just as clicking on the label (really, listitem) does) and
930 also syncs this checkbox with the right pane Enabled checkbox.
933 list_checkbox_cb (GtkWidget *cb, gpointer client_data)
935 prefs_pair *pair = (prefs_pair *) client_data;
937 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
938 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
940 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
941 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
942 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
946 GtkToggleButton *enabled =
947 GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
949 int which = gtk_list_child_position (list, line);
951 /* remember previous scroll position of the top of the list */
952 adj = gtk_scrolled_window_get_vadjustment (scroller);
953 scroll_top = adj->value;
955 apply_changes_and_save (GTK_WIDGET (list));
956 gtk_list_select_item (list, which);
957 /* ensure_selected_item_visible (GTK_WIDGET (list)); */
958 populate_demo_window (GTK_WIDGET (list), which, pair);
960 updating_enabled_cb++;
961 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
962 GTK_TOGGLE_BUTTON (cb)->active);
963 updating_enabled_cb--;
965 /* restore the previous scroll position of the top of the list.
966 this is weak, but I don't really know why it's moving... */
967 gtk_adjustment_set_value (adj, scroll_top);
971 /* Called when the right pane Enabled checkbox is clicked. This syncs
972 the corresponding checkbox inside the scrolling list to the state
976 enabled_cb (GtkWidget *cb, gpointer client_data)
978 int which = selected_hack_number (cb);
980 if (updating_enabled_cb) return;
984 GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
985 GList *kids = GTK_LIST (list)->children;
986 GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
987 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
988 GtkWidget *line_check =
989 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
991 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
992 GTK_TOGGLE_BUTTON (cb)->active);
997 /* Populating the various widgets
1001 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
1004 format_time (char *buf, Time time)
1006 int s = time / 1000;
1007 unsigned int h = 0, m = 0;
1018 sprintf (buf, "%u:%02u:%02u", h, m, s);
1023 make_pretty_name (const char *shell_command)
1025 char *s = strdup (shell_command);
1029 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1036 s2 = strrchr (s, '/'); /* if pathname, take last component */
1044 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1047 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1048 s2 = get_string_resource (res_name, res_name);
1052 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1053 if (*s2 >= 'A' && *s2 <= 'Z')
1056 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1058 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1064 /* Finds the number of the last hack to run, and makes that item be
1065 selected by default.
1068 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
1070 saver_preferences *p = pair->a;
1073 unsigned long nitems, bytesafter;
1075 Display *dpy = gdk_display;
1079 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1080 XA_SCREENSAVER_STATUS,
1081 0, 3, False, XA_INTEGER,
1082 &type, &format, &nitems, &bytesafter,
1083 (unsigned char **) &data)
1085 && type == XA_INTEGER
1088 which = (int) data[2] - 1;
1090 if (data) free (data);
1095 list = GTK_LIST (name_to_widget (toplevel, "list"));
1096 apply_changes_and_save (toplevel);
1097 if (which < p->screenhacks_count)
1099 gtk_list_select_item (list, which);
1100 ensure_selected_item_visible (GTK_WIDGET (list));
1101 populate_demo_window (toplevel, which, pair);
1108 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1110 saver_preferences *p = pair->a;
1111 GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1112 screenhack **hacks = p->screenhacks;
1115 for (h = hacks; h && *h; h++)
1117 /* A GtkList must contain only GtkListItems, but those can contain
1118 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1119 and a Label. We handle single and double click events on the
1120 line itself, for clicking on the text, but the interior checkbox
1121 also handles its own events.
1124 GtkWidget *line_hbox;
1125 GtkWidget *line_check;
1126 GtkWidget *line_label;
1128 char *pretty_name = (h[0]->name
1129 ? strdup (h[0]->name)
1130 : make_pretty_name (h[0]->command));
1132 line = gtk_list_item_new ();
1133 line_hbox = gtk_hbox_new (FALSE, 0);
1134 line_check = gtk_check_button_new ();
1135 line_label = gtk_label_new (pretty_name);
1137 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1138 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1139 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1141 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1143 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1145 gtk_widget_show (line_check);
1146 gtk_widget_show (line_label);
1147 gtk_widget_show (line_hbox);
1148 gtk_widget_show (line);
1152 gtk_container_add (GTK_CONTAINER (list), line);
1153 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1154 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1157 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1158 GTK_SIGNAL_FUNC (list_checkbox_cb),
1162 GTK_WIDGET (GTK_BIN(line)->child)->style =
1163 gtk_style_copy (GTK_WIDGET (text_line)->style);
1165 gtk_widget_show (line);
1168 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1169 GTK_SIGNAL_FUNC (list_select_cb),
1171 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1172 GTK_SIGNAL_FUNC (list_unselect_cb),
1178 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1180 saver_preferences *p = pair->a;
1183 format_time (s, p->timeout);
1184 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1185 format_time (s, p->cycle);
1186 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1187 format_time (s, p->lock_timeout);
1188 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1189 format_time (s, p->passwd_timeout);
1190 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "pass_text")), s);
1191 format_time (s, p->fade_seconds);
1192 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1193 sprintf (s, "%u", p->fade_ticks);
1194 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "ticks_text")), s);
1196 gtk_toggle_button_set_active (
1197 GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1199 gtk_toggle_button_set_active (
1200 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1202 gtk_toggle_button_set_active (
1203 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1205 gtk_toggle_button_set_active (
1206 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1208 gtk_toggle_button_set_active (
1209 GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1214 Bool found_any_writable_cells = False;
1215 Display *dpy = gdk_display;
1216 int nscreens = ScreenCount(dpy);
1218 for (i = 0; i < nscreens; i++)
1220 Screen *s = ScreenOfDisplay (dpy, i);
1221 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1223 found_any_writable_cells = True;
1228 gtk_widget_set_sensitive (
1229 GTK_WIDGET (name_to_widget (top, "fade_label")),
1230 found_any_writable_cells);
1231 gtk_widget_set_sensitive (
1232 GTK_WIDGET (name_to_widget (top, "ticks_label")),
1233 found_any_writable_cells);
1234 gtk_widget_set_sensitive (
1235 GTK_WIDGET (name_to_widget (top, "fade_text")),
1236 found_any_writable_cells);
1237 gtk_widget_set_sensitive (
1238 GTK_WIDGET (name_to_widget (top, "ticks_text")),
1239 found_any_writable_cells);
1240 gtk_widget_set_sensitive (
1241 GTK_WIDGET (name_to_widget (top, "install_button")),
1242 found_any_writable_cells);
1243 gtk_widget_set_sensitive (
1244 GTK_WIDGET (name_to_widget (top, "fade_button")),
1245 found_any_writable_cells);
1246 gtk_widget_set_sensitive (
1247 GTK_WIDGET (name_to_widget (top, "unfade_button")),
1248 found_any_writable_cells);
1255 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1257 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1258 "visual", "visual_combo",
1261 for (i = 0; i < countof(names); i++)
1263 GtkWidget *w = name_to_widget (toplevel, names[i]);
1264 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1267 /* I don't know how to handle these yet... */
1269 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1270 for (i = 0; i < countof(names2); i++)
1272 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1273 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1279 /* Even though we've given these text fields a maximum number of characters,
1280 their default size is still about 30 characters wide -- so measure out
1281 a string in their font, and resize them to just fit that.
1284 fix_text_entry_sizes (GtkWidget *toplevel)
1286 const char *names[] = { "timeout_text", "cycle_text", "fade_text",
1287 "ticks_text", "lock_text", "pass_text" };
1292 for (i = 0; i < countof(names); i++)
1294 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1296 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1297 gtk_widget_set_usize (w, width, -2);
1300 /* Now fix the size of the combo box.
1302 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1303 w = GTK_COMBO (w)->entry;
1304 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1305 gtk_widget_set_usize (w, width, -2);
1308 /* Now fix the size of the list.
1310 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1311 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1312 gtk_widget_set_usize (w, width, -2);
1319 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1322 static char *up_arrow_xpm[] = {
1345 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1346 the end of the array (Gtk 1.2.5.) */
1347 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1348 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1351 static char *down_arrow_xpm[] = {
1374 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1375 the end of the array (Gtk 1.2.5.) */
1376 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1377 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1381 pixmapify_button (GtkWidget *toplevel, int down_p)
1385 GtkWidget *pixmapwid;
1389 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1390 (down_p ? "next" : "prev")));
1391 style = gtk_widget_get_style (w);
1393 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1394 &style->bg[GTK_STATE_NORMAL],
1396 ? (gchar **) down_arrow_xpm
1397 : (gchar **) up_arrow_xpm));
1398 pixmapwid = gtk_pixmap_new (pixmap, mask);
1399 gtk_widget_show (pixmapwid);
1400 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1401 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1405 map_next_button_cb (GtkWidget *w, gpointer user_data)
1407 pixmapify_button (w, 1);
1411 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1413 pixmapify_button (w, 0);
1418 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1422 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1423 GtkAllocation *allocation,
1427 GtkWidgetAuxInfo *aux_info;
1429 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1431 aux_info->width = allocation->width;
1432 aux_info->height = -2;
1436 gtk_widget_size_request (label, &req);
1440 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1443 eschew_gtk_lossage (GtkWidget *toplevel)
1445 GtkWidgetAuxInfo *aux_info;
1446 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1448 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1449 aux_info->width = label->allocation.width;
1450 aux_info->height = -2;
1454 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1456 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1457 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1459 gtk_widget_queue_resize (label);
1464 get_hack_blurb (screenhack *hack)
1467 char *prog_name = strdup (hack->command);
1468 char *pretty_name = (hack->name
1469 ? strdup (hack->name)
1470 : make_pretty_name (hack->command));
1471 char doc_name[255], doc_class[255];
1474 for (s = prog_name; *s && !isspace(*s); s++)
1477 s = strrchr (prog_name, '/');
1478 if (s) strcpy (prog_name, s+1);
1480 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1481 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1485 doc_string = get_string_resource (doc_name, doc_class);
1488 for (s = doc_string; *s; s++)
1492 /* skip over whitespace at beginning of line */
1494 while (*s && (*s == ' ' || *s == '\t'))
1497 else if (*s == ' ' || *s == '\t')
1499 /* compress all other horizontal whitespace. */
1502 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1504 if (s2 > s) strcpy (s, s2);
1509 while (*s && isspace (*s)) /* Strip trailing whitespace */
1512 /* Delete whitespace at end of each line. */
1513 for (; s > doc_string; s--)
1514 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1517 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1521 if (s2 < s) strcpy (s2, s);
1525 /* Delete leading blank lines. */
1526 for (s = doc_string; *s == '\n'; s++)
1528 if (s > doc_string) strcpy (doc_string, s);
1532 static int doc_installed = 0;
1533 if (doc_installed == 0)
1535 if (get_boolean_resource ("hacks.documentation.isInstalled",
1536 "hacks.documentation.isInstalled"))
1542 if (doc_installed < 0)
1544 strdup ("Error:\n\n"
1545 "The documentation strings do not appear to be "
1546 "installed. This is probably because there is "
1547 "an \"XScreenSaver\" app-defaults file installed "
1548 "that is from an older version of the program. "
1549 "To fix this problem, delete that file, or "
1550 "install a current version (either will work.)");
1552 doc_string = strdup ("");
1560 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1562 saver_preferences *p = pair->a;
1563 screenhack *hack = (which >= 0 && which < p->screenhacks_count
1564 ? p->screenhacks[which] : 0);
1565 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1566 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1567 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1568 GtkToggleButton *enabled =
1569 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1570 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1572 char *pretty_name = (hack
1574 ? strdup (hack->name)
1575 : make_pretty_name (hack->command))
1577 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1579 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1580 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1581 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1582 gtk_entry_set_position (cmd, 0);
1584 updating_enabled_cb++;
1585 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1586 updating_enabled_cb--;
1588 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1590 ? (hack->visual && *hack->visual
1595 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1597 sensitize_demo_widgets (toplevel, (hack ? True : False));
1599 if (pretty_name) free (pretty_name);
1600 if (doc_string) free (doc_string);
1602 _selected_hack_number = which;
1607 widget_deleter (GtkWidget *widget, gpointer data)
1609 /* #### Well, I want to destroy these widgets, but if I do that, they get
1610 referenced again, and eventually I get a SEGV. So instead of
1611 destroying them, I'll just hide them, and leak a bunch of memory
1612 every time the disk file changes. Go go go Gtk!
1614 #### Ok, that's a lie, I get a crash even if I just hide the widget
1615 and don't ever delete it. Fuck!
1618 gtk_widget_destroy (widget);
1620 gtk_widget_hide (widget);
1626 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1629 saver_preferences *p = pair->a;
1631 static Bool reentrant_lock = False;
1632 if (reentrant_lock) return 0;
1633 reentrant_lock = True;
1635 if (init_file_changed_p (p))
1637 const char *f = init_file_name();
1642 if (!f || !*f) return 0;
1643 b = (char *) malloc (strlen(f) + 1024);
1646 "file \"%s\" has changed, reloading.\n",
1648 warning_dialog (widget, b, False, 100);
1653 which = selected_hack_number (widget);
1654 list = GTK_LIST (name_to_widget (widget, "list"));
1655 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1656 populate_hack_list (widget, pair);
1657 gtk_list_select_item (list, which);
1658 populate_prefs_page (widget, pair);
1659 populate_demo_window (widget, which, pair);
1660 ensure_selected_item_visible (GTK_WIDGET (list));
1665 reentrant_lock = False;
1671 /* The main demo-mode command loop.
1676 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1677 XrmRepresentation *type, XrmValue *value, XPointer closure)
1680 for (i = 0; quarks[i]; i++)
1682 if (bindings[i] == XrmBindTightly)
1683 fprintf (stderr, (i == 0 ? "" : "."));
1684 else if (bindings[i] == XrmBindLoosely)
1685 fprintf (stderr, "*");
1687 fprintf (stderr, " ??? ");
1688 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1691 fprintf (stderr, ": %s\n", (char *) value->addr);
1699 the_network_is_not_the_computer (GtkWidget *parent)
1701 Display *dpy = gdk_display;
1702 char *rversion, *ruser, *rhost;
1703 char *luser, *lhost;
1705 struct passwd *p = getpwuid (getuid ());
1706 const char *d = DisplayString (dpy);
1708 # if defined(HAVE_UNAME)
1710 if (uname (&uts) < 0)
1711 lhost = "<UNKNOWN>";
1713 lhost = uts.nodename;
1715 strcpy (lhost, getenv("SYS$NODE"));
1716 # else /* !HAVE_UNAME && !VMS */
1717 strcat (lhost, "<UNKNOWN>");
1718 # endif /* !HAVE_UNAME && !VMS */
1720 if (p && p->pw_name)
1725 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1727 /* Make a buffer that's big enough for a number of copies of all the
1728 strings, plus some. */
1729 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1730 (ruser ? strlen(ruser) : 0) +
1731 (rhost ? strlen(rhost) : 0) +
1738 if (!rversion || !*rversion)
1742 "The XScreenSaver daemon doesn't seem to be running\n"
1743 "on display \"%s\". Launch it now?",
1746 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1748 /* Warn that the two processes are running as different users.
1752 "%s is running as user \"%s\" on host \"%s\".\n"
1753 "But the xscreensaver managing display \"%s\"\n"
1754 "is running as user \"%s\" on host \"%s\".\n"
1756 "Since they are different users, they won't be reading/writing\n"
1757 "the same ~/.xscreensaver file, so %s isn't\n"
1758 "going to work right.\n"
1760 "You should either re-run %s as \"%s\", or re-run\n"
1761 "xscreensaver as \"%s\".\n"
1763 "Restart the xscreensaver daemon now?\n",
1764 progname, luser, lhost,
1766 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1768 progname, (ruser ? ruser : "???"),
1771 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1773 /* Warn that the two processes are running on different hosts.
1777 "%s is running as user \"%s\" on host \"%s\".\n"
1778 "But the xscreensaver managing display \"%s\"\n"
1779 "is running as user \"%s\" on host \"%s\".\n"
1781 "If those two machines don't share a file system (that is,\n"
1782 "if they don't see the same ~%s/.xscreensaver file) then\n"
1783 "%s won't work right.\n"
1785 "Restart the daemon on \"%s\" as \"%s\" now?\n",
1786 progname, luser, lhost,
1788 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1793 else if (!!strcmp (rversion, short_version))
1795 /* Warn that the version numbers don't match.
1799 "This is %s version %s.\n"
1800 "But the xscreensaver managing display \"%s\"\n"
1801 "is version %s. This could cause problems.\n"
1803 "Restart the xscreensaver daemon now?\n",
1804 progname, short_version,
1811 warning_dialog (parent, msg, True, 1);
1817 /* We use this error handler so that X errors are preceeded by the name
1818 of the program that generated them.
1821 demo_ehandler (Display *dpy, XErrorEvent *error)
1823 fprintf (stderr, "\nX error in %s:\n", progname);
1824 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1827 fprintf (stderr, " (nonfatal.)\n");
1832 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
1833 of the program that generated them; and also that we can ignore one
1834 particular bogus error message that Gdk madly spews.
1837 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
1838 const gchar *message, gpointer user_data)
1840 /* Ignore the message "Got event for unknown window: 0x...".
1841 Apparently some events are coming in for the xscreensaver window
1842 (presumably reply events related to the ClientMessage) and Gdk
1843 feels the need to complain about them. So, just suppress any
1844 messages that look like that one.
1846 if (strstr (message, "unknown window"))
1849 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
1850 (log_level == G_LOG_LEVEL_ERROR ? "error" :
1851 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
1852 log_level == G_LOG_LEVEL_WARNING ? "warning" :
1853 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
1854 log_level == G_LOG_LEVEL_INFO ? "info" :
1855 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
1857 ((!*message || message[strlen(message)-1] != '\n')
1862 static char *defaults[] = {
1863 #include "XScreenSaver_ad.h"
1868 #ifdef HAVE_CRAPPLET
1869 static struct poptOption crapplet_options[] = {
1870 {NULL, '\0', 0, NULL, 0}
1872 #endif /* HAVE_CRAPPLET */
1876 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
1881 map_window_cb (GtkWidget *w, gpointer user_data)
1883 Boolean oi = initializing_p;
1884 initializing_p = True;
1885 eschew_gtk_lossage (w);
1886 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
1887 initializing_p = oi;
1892 main (int argc, char **argv)
1895 prefs_pair Pair, *pair;
1896 saver_preferences P, P2, *p, *p2;
1900 Widget toplevel_shell;
1901 GtkWidget *gtk_window;
1902 char *real_progname = argv[0];
1905 initializing_p = True;
1907 s = strrchr (real_progname, '/');
1908 if (s) real_progname = s+1;
1915 memset (p, 0, sizeof (*p));
1916 memset (p2, 0, sizeof (*p2));
1918 global_prefs_pair = pair; /* I hate C so much... */
1920 progname = real_progname;
1922 short_version = (char *) malloc (5);
1923 memcpy (short_version, screensaver_id + 17, 4);
1924 short_version [4] = 0;
1927 /* Register our error message logger for every ``log domain'' known.
1928 There's no way to do this globally, so I grepped the Gtk/Gdk sources
1929 for all of the domains that seem to be in use.
1932 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
1933 "GThread", "Gnome", "GnomeUI", 0 };
1934 for (i = 0; domains[i]; i++)
1935 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
1938 /* This is gross, but Gtk understands --display and not -display...
1940 for (i = 1; i < argc; i++)
1941 if (argv[i][0] && argv[i][1] &&
1942 !strncmp(argv[i], "-display", strlen(argv[i])))
1943 argv[i] = "--display";
1946 /* We need to parse this arg really early... Sigh. */
1947 for (i = 1; i < argc; i++)
1949 (!strcmp(argv[i], "--crapplet") ||
1950 !strcmp(argv[i], "--capplet")))
1952 # ifdef HAVE_CRAPPLET
1955 for (j = i; j < argc; j++) /* remove it from the list */
1956 argv[j] = argv[j+1];
1959 # else /* !HAVE_CRAPPLET */
1960 fprintf (stderr, "%s: not compiled with --crapplet support\n",
1964 # endif /* !HAVE_CRAPPLET */
1967 /* Let Gtk open the X connection, then initialize Xt to use that
1968 same connection. Doctor Frankenstein would be proud.
1970 # ifdef HAVE_CRAPPLET
1973 GnomeClient *client;
1974 GnomeClientFlags flags = 0;
1976 int init_results = gnome_capplet_init ("screensaver-properties",
1978 argc, argv, NULL, 0, NULL);
1980 0 upon successful initialization;
1981 1 if --init-session-settings was passed on the cmdline;
1982 2 if --ignore was passed on the cmdline;
1985 So the 1 signifies just to init the settings, and quit, basically.
1986 (Meaning launch the xscreensaver daemon.)
1989 if (init_results < 0)
1992 g_error ("An initialization error occurred while "
1993 "starting xscreensaver-capplet.\n");
1995 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
1996 real_progname, init_results);
2001 client = gnome_master_client ();
2004 flags = gnome_client_get_flags (client);
2006 if (flags & GNOME_CLIENT_IS_CONNECTED)
2009 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2010 gnome_client_get_id (client));
2013 char *session_args[20];
2015 session_args[i++] = real_progname;
2016 session_args[i++] = "--capplet";
2017 session_args[i++] = "--init-session-settings";
2018 session_args[i] = 0;
2019 gnome_client_set_priority (client, 20);
2020 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2021 gnome_client_set_restart_command (client, i, session_args);
2025 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2028 gnome_client_flush (client);
2031 if (init_results == 1)
2033 system ("xscreensaver -nosplash &");
2039 # endif /* HAVE_CRAPPLET */
2041 gtk_init (&argc, &argv);
2045 /* We must read exactly the same resources as xscreensaver.
2046 That means we must have both the same progclass *and* progname,
2047 at least as far as the resource database is concerned. So,
2048 put "xscreensaver" in argv[0] while initializing Xt.
2050 argv[0] = "xscreensaver";
2054 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2056 XtToolkitInitialize ();
2057 app = XtCreateApplicationContext ();
2059 XtAppSetFallbackResources (app, defaults);
2060 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2061 toplevel_shell = XtAppCreateShell (progname, progclass,
2062 applicationShellWidgetClass,
2065 dpy = XtDisplay (toplevel_shell);
2066 db = XtDatabase (dpy);
2067 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2068 XSetErrorHandler (demo_ehandler);
2071 /* After doing Xt-style command-line processing, complain about any
2072 unrecognized command-line arguments.
2074 for (i = 1; i < argc; i++)
2077 if (s[0] == '-' && s[1] == '-')
2079 if (!strcmp (s, "-prefs"))
2081 else if (crapplet_p)
2082 /* There are lots of random args that we don't care about when we're
2083 started as a crapplet, so just ignore unknown args in that case. */
2087 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2093 /* Load the init file, which may end up consulting the X resource database
2094 and the site-wide app-defaults file. Note that at this point, it's
2095 important that `progname' be "xscreensaver", rather than whatever
2102 /* Now that Xt has been initialized, and the resources have been read,
2103 we can set our `progname' variable to something more in line with
2106 progname = real_progname;
2110 /* Print out all the resources we read. */
2112 XrmName name = { 0 };
2113 XrmClass class = { 0 };
2115 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2121 /* Intern the atoms that xscreensaver_command() needs.
2123 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2124 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2125 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2126 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2127 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2128 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2129 XA_SELECT = XInternAtom (dpy, "SELECT", False);
2130 XA_DEMO = XInternAtom (dpy, "DEMO", False);
2131 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2132 XA_BLANK = XInternAtom (dpy, "BLANK", False);
2133 XA_LOCK = XInternAtom (dpy, "LOCK", False);
2134 XA_EXIT = XInternAtom (dpy, "EXIT", False);
2135 XA_RESTART = XInternAtom (dpy, "RESTART", False);
2138 /* Create the window and all its widgets.
2140 gtk_window = create_xscreensaver_demo ();
2141 toplevel_widget = gtk_window;
2143 /* Set the window's title. */
2146 char *v = (char *) strdup(strchr(screensaver_id, ' '));
2147 char *s1, *s2, *s3, *s4;
2148 s1 = (char *) strchr(v, ' '); s1++;
2149 s2 = (char *) strchr(s1, ' ');
2150 s3 = (char *) strchr(v, '('); s3++;
2151 s4 = (char *) strchr(s3, ')');
2154 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2155 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2159 /* Various other widget initializations...
2161 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2162 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2164 populate_hack_list (gtk_window, pair);
2165 populate_prefs_page (gtk_window, pair);
2166 sensitize_demo_widgets (gtk_window, False);
2167 fix_text_entry_sizes (gtk_window);
2168 scroll_to_current_hack (gtk_window, pair);
2170 gtk_signal_connect (
2171 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2172 "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2173 gtk_signal_connect (
2174 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2175 "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2176 gtk_signal_connect (
2177 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2178 "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2181 /* Handle the -prefs command-line argument. */
2184 GtkNotebook *notebook =
2185 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2186 gtk_notebook_set_page (notebook, 1);
2189 # ifdef HAVE_CRAPPLET
2193 GtkWidget *top_vbox;
2195 capplet = capplet_widget_new ();
2197 top_vbox = GTK_BIN (gtk_window)->child;
2199 gtk_widget_ref (top_vbox);
2200 gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2201 GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2203 /* In crapplet-mode, take off the menubar. */
2204 gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2206 gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2207 gtk_widget_show (capplet);
2208 gtk_widget_hide (gtk_window);
2210 /* Hook up the Control Center's redundant Help button, too. */
2211 gtk_signal_connect (GTK_OBJECT (capplet), "help",
2212 GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2214 /* Issue any warnings about the running xscreensaver daemon. */
2215 the_network_is_not_the_computer (top_vbox);
2218 # endif /* HAVE_CRAPPLET */
2220 gtk_widget_show (gtk_window);
2222 /* Issue any warnings about the running xscreensaver daemon. */
2223 the_network_is_not_the_computer (gtk_window);
2226 /* Run the Gtk event loop, and not the Xt event loop. This means that
2227 if there were Xt timers or fds registered, they would never get serviced,
2228 and if there were any Xt widgets, they would never have events delivered.
2229 Fortunately, we're using Gtk for all of the UI, and only initialized
2230 Xt so that we could process the command line and use the X resource
2233 initializing_p = False;
2235 # ifdef HAVE_CRAPPLET
2237 capplet_gtk_main ();
2239 # endif /* HAVE_CRAPPLET */
2245 #endif /* HAVE_GTK -- whole file */