1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2001 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifdef HAVE_GTK /* whole file */
26 # include <pwd.h> /* for getpwuid() */
32 # include <sys/utsname.h> /* for uname() */
33 #endif /* HAVE_UNAME */
37 #include <X11/Xproto.h> /* for CARD32 */
38 #include <X11/Xatom.h> /* for XA_INTEGER */
39 #include <X11/Intrinsic.h>
40 #include <X11/StringDefs.h>
42 /* We don't actually use any widget internals, but these are included
43 so that gdb will have debug info for the widgets... */
44 #include <X11/IntrinsicP.h>
45 #include <X11/ShellP.h>
49 # include <X11/Xmu/Error.h>
51 # include <Xmu/Error.h>
61 # include <capplet-widget.h>
64 extern Display *gdk_display;
68 #include "resources.h" /* for parse_time() */
69 #include "visual.h" /* for has_writable_cells() */
70 #include "remote.h" /* for xscreensaver_command() */
73 #include "logo-50.xpm"
74 #include "logo-180.xpm"
76 #include "demo-Gtk-widgets.h"
83 #define countof(x) (sizeof((x))/sizeof((*x)))
87 char *progclass = "XScreenSaver";
90 static Bool crapplet_p = False;
91 static Bool initializing_p;
92 static GtkWidget *toplevel_widget;
95 saver_preferences *a, *b;
98 static void *global_prefs_pair; /* I hate C so much... */
100 char *blurb (void) { return progname; }
102 static char *short_version = 0;
105 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
106 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
107 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
110 static void populate_demo_window (GtkWidget *toplevel,
111 int which, prefs_pair *pair);
112 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
113 static int apply_changes_and_save (GtkWidget *widget);
114 static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair);
115 static void await_xscreensaver (GtkWidget *widget);
118 /* Some random utility functions
122 name_to_widget (GtkWidget *widget, const char *name)
124 return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
128 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
129 Takes a scroller, viewport, or list as an argument.
132 ensure_selected_item_visible (GtkWidget *widget)
134 GtkScrolledWindow *scroller = 0;
136 GtkList *list_widget = 0;
140 GtkWidget *selected = 0;
143 gint parent_h, child_y, child_h, children_h, ignore;
144 double ratio_t, ratio_b;
146 if (GTK_IS_SCROLLED_WINDOW (widget))
148 scroller = GTK_SCROLLED_WINDOW (widget);
149 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
150 list_widget = GTK_LIST (GTK_BIN(vp)->child);
152 else if (GTK_IS_VIEWPORT (widget))
154 vp = GTK_VIEWPORT (widget);
155 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
156 list_widget = GTK_LIST (GTK_BIN(vp)->child);
158 else if (GTK_IS_LIST (widget))
160 list_widget = GTK_LIST (widget);
161 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
162 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
167 slist = list_widget->selection;
168 selected = (slist ? GTK_WIDGET (slist->data) : 0);
172 which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
174 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
175 kids; kids = kids->next)
178 adj = gtk_scrolled_window_get_vadjustment (scroller);
180 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
181 &ignore, &ignore, &ignore, &parent_h, &ignore);
182 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
183 &ignore, &child_y, &ignore, &child_h, &ignore);
184 children_h = nkids * child_h;
186 ratio_t = ((double) child_y) / ((double) children_h);
187 ratio_b = ((double) child_y + child_h) / ((double) children_h);
189 if (adj->upper == 0.0) /* no items in list */
192 if (ratio_t < (adj->value / adj->upper) ||
193 ratio_b > ((adj->value + adj->page_size) / adj->upper))
196 int slop = parent_h * 0.75; /* how much to overshoot by */
198 if (ratio_t < (adj->value / adj->upper))
200 double ratio_w = ((double) parent_h) / ((double) children_h);
201 double ratio_l = (ratio_b - ratio_t);
202 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
205 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
207 target = ratio_t * adj->upper;
211 if (target > adj->upper - adj->page_size)
212 target = adj->upper - adj->page_size;
216 gtk_adjustment_set_value (adj, target);
221 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
223 GtkWidget *shell = GTK_WIDGET (user_data);
224 while (shell->parent)
225 shell = shell->parent;
226 gtk_widget_destroy (GTK_WIDGET (shell));
230 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
232 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
234 restart_menu_cb (widget, user_data);
235 warning_dialog_dismiss_cb (widget, user_data);
239 warning_dialog (GtkWidget *parent, const char *message,
240 Boolean restart_button_p, int center)
242 char *msg = strdup (message);
245 GtkWidget *dialog = gtk_dialog_new ();
246 GtkWidget *label = 0;
248 GtkWidget *cancel = 0;
251 while (parent->parent)
252 parent = parent->parent;
258 char *s = strchr (head, '\n');
261 sprintf (name, "label%d", i++);
264 label = gtk_label_new (head);
268 GTK_WIDGET (label)->style =
269 gtk_style_copy (GTK_WIDGET (label)->style);
270 GTK_WIDGET (label)->style->font =
271 gdk_font_load (get_string_resource("warning_dialog.headingFont",
273 gtk_widget_set_style (GTK_WIDGET (label),
274 GTK_WIDGET (label)->style);
278 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
279 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
280 label, TRUE, TRUE, 0);
281 gtk_widget_show (label);
292 label = gtk_label_new ("");
293 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
294 label, TRUE, TRUE, 0);
295 gtk_widget_show (label);
297 label = gtk_hbutton_box_new ();
298 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
299 label, TRUE, TRUE, 0);
301 ok = gtk_button_new_with_label ("OK");
302 gtk_container_add (GTK_CONTAINER (label), ok);
304 if (restart_button_p)
306 cancel = gtk_button_new_with_label ("Cancel");
307 gtk_container_add (GTK_CONTAINER (label), cancel);
310 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
311 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
312 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
313 gtk_widget_show (ok);
315 gtk_widget_show (cancel);
316 gtk_widget_show (label);
317 gtk_widget_show (dialog);
318 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
320 if (restart_button_p)
322 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
323 GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
325 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
326 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
331 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
332 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
335 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
336 GTK_WIDGET (parent)->window);
338 gdk_window_show (GTK_WIDGET (dialog)->window);
339 gdk_window_raise (GTK_WIDGET (dialog)->window);
346 run_cmd (GtkWidget *widget, Atom command, int arg)
351 apply_changes_and_save (widget);
352 status = xscreensaver_command (gdk_display, command, arg, False, &err);
357 sprintf (buf, "Error:\n\n%s", err);
359 strcpy (buf, "Unknown error!");
360 warning_dialog (widget, buf, False, 100);
367 run_hack (GtkWidget *widget, int which, Bool report_errors_p)
369 if (which < 0) return;
370 apply_changes_and_save (widget);
372 run_cmd (widget, XA_DEMO, which + 1);
376 xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s);
387 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
389 apply_changes_and_save (GTK_WIDGET (menuitem));
394 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
396 apply_changes_and_save (widget);
402 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
405 warning_dialog (GTK_WIDGET (menuitem),
407 "cut unimplemented\n", False, 1);
412 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
415 warning_dialog (GTK_WIDGET (menuitem),
417 "copy unimplemented\n", False, 1);
422 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
425 warning_dialog (GTK_WIDGET (menuitem),
427 "paste unimplemented\n", False, 1);
432 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
435 char *vers = strdup (screensaver_id + 4);
438 char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
440 s = strchr (vers, ',');
444 sprintf(copy, "Copyright \251 1991-2001 %s", s);
446 sprintf (msg, "%s\n\n%s", copy, desc);
448 /* I can't make gnome_about_new() work here -- it starts dying in
449 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
450 then this might be the thing to do:
454 const gchar *auth[] = { 0 };
455 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
457 gtk_widget_show (about);
459 #else / * GTK but not GNOME * /
463 GdkColormap *colormap;
464 GdkPixmap *gdkpixmap;
467 GtkWidget *dialog = gtk_dialog_new ();
468 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
469 GtkWidget *parent = GTK_WIDGET (menuitem);
470 while (parent->parent)
471 parent = parent->parent;
473 hbox = gtk_hbox_new (FALSE, 20);
474 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
475 hbox, TRUE, TRUE, 0);
477 colormap = gtk_widget_get_colormap (parent);
479 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
480 (gchar **) logo_180_xpm);
481 icon = gtk_pixmap_new (gdkpixmap, mask);
482 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
484 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
486 vbox = gtk_vbox_new (FALSE, 0);
487 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
489 label1 = gtk_label_new (vers);
490 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
491 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
492 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
494 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
495 GTK_WIDGET (label1)->style->font =
496 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
497 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
499 label2 = gtk_label_new (msg);
500 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
501 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
502 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
504 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
505 GTK_WIDGET (label2)->style->font =
506 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
507 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
509 hb = gtk_hbutton_box_new ();
511 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
514 ok = gtk_button_new_with_label ("OK");
515 gtk_container_add (GTK_CONTAINER (hb), ok);
517 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
518 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
519 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
521 gtk_widget_show (hbox);
522 gtk_widget_show (icon);
523 gtk_widget_show (vbox);
524 gtk_widget_show (label1);
525 gtk_widget_show (label2);
526 gtk_widget_show (hb);
527 gtk_widget_show (ok);
528 gtk_widget_show (dialog);
530 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
531 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
533 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
534 GTK_WIDGET (parent)->window);
535 gdk_window_show (GTK_WIDGET (dialog)->window);
536 gdk_window_raise (GTK_WIDGET (dialog)->window);
542 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
544 /* prefs_pair *pair = (prefs_pair *) client_data; */
545 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
547 saver_preferences *p = pair->a;
550 if (!p->help_url || !*p->help_url)
552 warning_dialog (GTK_WIDGET (menuitem),
554 "No Help URL has been specified.\n", False, 100);
558 help_command = (char *) malloc (strlen (p->load_url_command) +
559 (strlen (p->help_url) * 2) + 20);
560 strcpy (help_command, "( ");
561 sprintf (help_command + strlen(help_command),
562 p->load_url_command, p->help_url, p->help_url);
563 strcat (help_command, " ) &");
564 system (help_command);
570 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
572 run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
577 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
579 run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
584 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
586 run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
591 restart_menu_cb (GtkWidget *widget, gpointer user_data)
594 run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
596 apply_changes_and_save (GTK_WIDGET (widget));
597 xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL);
599 system ("xscreensaver -nosplash &");
602 await_xscreensaver (GTK_WIDGET (widget));
606 await_xscreensaver (GtkWidget *widget)
610 Display *dpy = gdk_display;
611 /* GtkWidget *dialog = 0;*/
614 while (!rversion && (--countdown > 0))
616 /* Check for the version of the running xscreensaver... */
617 server_xscreensaver_version (dpy, &rversion, 0, 0);
619 /* If it's not there yet, wait a second... */
623 /* if (dialog) gtk_widget_destroy (dialog);*/
632 /* Timed out, no screensaver running. */
635 Bool root_p = (geteuid () == 0);
639 "The xscreensaver daemon did not start up properly.\n"
644 "You are running as root. This usually means that xscreensaver\n"
645 "was unable to contact your X server because access control is\n"
646 "turned on. Try running this command:\n"
648 " xhost +localhost\n"
650 "and then selecting `File / Restart Daemon'.\n"
652 "Note that turning off access control will allow anyone logged\n"
653 "on to this machine to access your screen, which might be\n"
654 "considered a security problem. Please read the xscreensaver\n"
655 "manual and FAQ for more information.\n"
657 "You shouldn't run X as root. Instead, you should log in as a\n"
658 "normal user, and `su' as necessary.");
660 strcat (buf, "Please check your $PATH and permissions.");
662 warning_dialog (widget, buf, False, 1);
667 static int _selected_hack_number = -1;
670 selected_hack_number (GtkWidget *toplevel)
673 GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
674 GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
675 GList *slist = list_widget->selection;
676 GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
677 int which = (selected
678 ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
682 return _selected_hack_number;
688 demo_write_init_file (GtkWidget *widget, saver_preferences *p)
690 if (!write_init_file (p, short_version, False))
694 const char *f = init_file_name();
696 warning_dialog (widget,
697 "Error:\n\nCouldn't determine init file name!\n",
701 char *b = (char *) malloc (strlen(f) + 1024);
702 sprintf (b, "Error:\n\nCouldn't write %s\n", f);
703 warning_dialog (widget, b, False, 100);
712 apply_changes_and_save_1 (GtkWidget *widget)
714 /* prefs_pair *pair = (prefs_pair *) client_data; */
715 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
716 saver_preferences *p = pair->a;
717 GtkList *list_widget =
718 GTK_LIST (name_to_widget (widget, "list"));
719 int which = selected_hack_number (widget);
721 GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
722 GtkToggleButton *enabled =
723 GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled"));
724 GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo"));
726 Bool enabled_p = gtk_toggle_button_get_active (enabled);
727 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
728 const char *command = gtk_entry_get_text (cmd);
733 if (which < 0) return -1;
735 if (maybe_reload_init_file (widget, pair) != 0)
738 /* Sanity-check and canonicalize whatever the user typed into the combo box.
740 if (!strcasecmp (visual, "")) visual = "";
741 else if (!strcasecmp (visual, "any")) visual = "";
742 else if (!strcasecmp (visual, "default")) visual = "Default";
743 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
744 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
745 else if (!strcasecmp (visual, "best")) visual = "Best";
746 else if (!strcasecmp (visual, "mono")) visual = "Mono";
747 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
748 else if (!strcasecmp (visual, "gray")) visual = "Gray";
749 else if (!strcasecmp (visual, "grey")) visual = "Gray";
750 else if (!strcasecmp (visual, "color")) visual = "Color";
751 else if (!strcasecmp (visual, "gl")) visual = "GL";
752 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
753 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
754 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
755 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
756 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
757 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
758 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
759 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
760 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
763 gdk_beep (); /* unparsable */
765 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
768 ensure_selected_item_visible (GTK_WIDGET (list_widget));
770 if (!p->screenhacks[which]->visual)
771 p->screenhacks[which]->visual = strdup ("");
772 if (!p->screenhacks[which]->command)
773 p->screenhacks[which]->command = strdup ("");
775 if (p->screenhacks[which]->enabled_p != enabled_p ||
776 !!strcasecmp (p->screenhacks[which]->visual, visual) ||
777 !!strcasecmp (p->screenhacks[which]->command, command))
779 /* Something was changed -- store results into the struct,
782 free (p->screenhacks[which]->visual);
783 free (p->screenhacks[which]->command);
784 p->screenhacks[which]->visual = strdup (visual);
785 p->screenhacks[which]->command = strdup (command);
786 p->screenhacks[which]->enabled_p = enabled_p;
788 return demo_write_init_file (widget, p);
791 /* No changes made */
795 void prefs_ok_cb (GtkButton *button, gpointer user_data);
798 apply_changes_and_save (GtkWidget *widget)
800 prefs_ok_cb ((GtkButton *) widget, 0);
801 return apply_changes_and_save_1 (widget);
806 run_this_cb (GtkButton *button, gpointer user_data)
808 int which = selected_hack_number (GTK_WIDGET (button));
809 if (which < 0) return;
810 if (0 == apply_changes_and_save (GTK_WIDGET (button)))
811 run_hack (GTK_WIDGET (button), which, True);
816 manual_cb (GtkButton *button, gpointer user_data)
818 /* prefs_pair *pair = (prefs_pair *) client_data; */
819 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
820 saver_preferences *p = pair->a;
821 GtkList *list_widget =
822 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
823 int which = selected_hack_number (GTK_WIDGET (button));
824 char *name, *name2, *cmd, *s;
825 if (which < 0) return;
826 apply_changes_and_save (GTK_WIDGET (button));
827 ensure_selected_item_visible (GTK_WIDGET (list_widget));
829 name = strdup (p->screenhacks[which]->command);
831 while (isspace (*name2)) name2++;
833 while (*s && !isspace (*s)) s++;
835 s = strrchr (name2, '/');
838 cmd = get_string_resource ("manualCommand", "ManualCommand");
841 char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
843 sprintf (cmd2 + strlen (cmd2),
845 name2, name2, name2, name2);
846 strcat (cmd2, " ) &");
852 warning_dialog (GTK_WIDGET (button),
853 "Error:\n\nno `manualCommand' resource set.",
862 run_next_cb (GtkButton *button, gpointer user_data)
864 /* prefs_pair *pair = (prefs_pair *) client_data; */
865 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
866 saver_preferences *p = pair->a;
868 GtkList *list_widget =
869 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
870 int which = selected_hack_number (GTK_WIDGET (button));
877 if (which >= p->screenhacks_count)
880 apply_changes_and_save (GTK_WIDGET (button));
881 gtk_list_select_item (GTK_LIST (list_widget), which);
882 ensure_selected_item_visible (GTK_WIDGET (list_widget));
883 populate_demo_window (GTK_WIDGET (button), which, pair);
884 run_hack (GTK_WIDGET (button), which, False);
889 run_prev_cb (GtkButton *button, gpointer user_data)
891 /* prefs_pair *pair = (prefs_pair *) client_data; */
892 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
893 saver_preferences *p = pair->a;
895 GtkList *list_widget =
896 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
897 int which = selected_hack_number (GTK_WIDGET (button));
900 which = p->screenhacks_count - 1;
905 which = p->screenhacks_count - 1;
907 apply_changes_and_save (GTK_WIDGET (button));
908 gtk_list_select_item (GTK_LIST (list_widget), which);
909 ensure_selected_item_visible (GTK_WIDGET (list_widget));
910 populate_demo_window (GTK_WIDGET (button), which, pair);
911 run_hack (GTK_WIDGET (button), which, False);
915 /* Helper for the text fields that contain time specifications:
916 this parses the text, and does error checking.
919 hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
924 value = parse_time ((char *) line, sec_p, True);
925 value *= 1000; /* Time measures in microseconds */
931 "Unparsable time format: \"%s\"\n",
933 warning_dialog (widget, b, False, 100);
942 prefs_ok_cb (GtkButton *button, gpointer user_data)
944 /* prefs_pair *pair = (prefs_pair *) client_data; */
945 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
947 saver_preferences *p = pair->a;
948 saver_preferences *p2 = pair->b;
949 Bool changed = False;
951 # define SECONDS(field, name) \
952 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
953 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
957 # define MINUTES(field, name) \
958 hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\
959 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
963 # define INTEGER(field, name) do { \
964 char *line = gtk_entry_get_text (\
965 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
966 unsigned int value; \
970 else if (sscanf (line, "%u%c", &value, &c) != 1) \
973 sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \
974 warning_dialog (GTK_WIDGET (button), b, False, 100); \
980 # define CHECKBOX(field, name) \
981 field = gtk_toggle_button_get_active (\
982 GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
984 MINUTES (&p2->timeout, "timeout_text");
985 MINUTES (&p2->cycle, "cycle_text");
986 CHECKBOX (p2->lock_p, "lock_button");
987 MINUTES (&p2->lock_timeout, "lock_text");
989 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
990 MINUTES (&p2->dpms_standby, "dpms_standby_text");
991 MINUTES (&p2->dpms_suspend, "dpms_suspend_text");
992 MINUTES (&p2->dpms_off, "dpms_off_text");
994 CHECKBOX (p2->verbose_p, "verbose_button");
995 CHECKBOX (p2->capture_stderr_p, "capture_button");
996 CHECKBOX (p2->splash_p, "splash_button");
998 CHECKBOX (p2->install_cmap_p, "install_button");
999 CHECKBOX (p2->fade_p, "fade_button");
1000 CHECKBOX (p2->unfade_p, "unfade_button");
1001 SECONDS (&p2->fade_seconds, "fade_text");
1008 # define COPY(field) \
1009 if (p->field != p2->field) changed = True; \
1010 p->field = p2->field
1017 COPY(dpms_enabled_p);
1023 COPY(capture_stderr_p);
1026 COPY(install_cmap_p);
1032 populate_prefs_page (GTK_WIDGET (button), pair);
1036 Display *dpy = gdk_display;
1037 sync_server_dpms_settings (dpy, p->dpms_enabled_p,
1038 p->dpms_standby / 1000,
1039 p->dpms_suspend / 1000,
1043 demo_write_init_file (GTK_WIDGET (button), p);
1049 prefs_cancel_cb (GtkButton *button, gpointer user_data)
1051 /* prefs_pair *pair = (prefs_pair *) client_data; */
1052 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
1054 *pair->b = *pair->a;
1055 populate_prefs_page (GTK_WIDGET (button), pair);
1060 pref_changed_cb (GtkButton *button, gpointer user_data)
1062 if (! initializing_p)
1063 apply_changes_and_save (GTK_WIDGET (button));
1068 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1069 gpointer client_data)
1071 if (event->type == GDK_2BUTTON_PRESS)
1073 GtkList *list = GTK_LIST (name_to_widget (button, "list"));
1074 int which = gtk_list_child_position (list, GTK_WIDGET (button));
1077 run_hack (GTK_WIDGET (button), which, True);
1085 list_select_cb (GtkList *list, GtkWidget *child)
1087 /* prefs_pair *pair = (prefs_pair *) client_data; */
1088 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
1090 int which = gtk_list_child_position (list, GTK_WIDGET (child));
1091 apply_changes_and_save (GTK_WIDGET (list));
1092 populate_demo_window (GTK_WIDGET (list), which, pair);
1096 list_unselect_cb (GtkList *list, GtkWidget *child)
1098 /* prefs_pair *pair = (prefs_pair *) client_data; */
1099 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
1101 apply_changes_and_save (GTK_WIDGET (list));
1102 populate_demo_window (GTK_WIDGET (list), -1, pair);
1106 static int updating_enabled_cb = 0; /* kludge to make sure that enabled_cb
1107 is only run by user action, not by
1110 /* Called when the checkboxes that are in the left column of the
1111 scrolling list are clicked. This both populates the right pane
1112 (just as clicking on the label (really, listitem) does) and
1113 also syncs this checkbox with the right pane Enabled checkbox.
1116 list_checkbox_cb (GtkWidget *cb, gpointer client_data)
1118 prefs_pair *pair = (prefs_pair *) client_data;
1120 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
1121 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
1123 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
1124 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
1125 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
1129 GtkToggleButton *enabled =
1130 GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
1132 int which = gtk_list_child_position (list, line);
1134 /* remember previous scroll position of the top of the list */
1135 adj = gtk_scrolled_window_get_vadjustment (scroller);
1136 scroll_top = adj->value;
1138 apply_changes_and_save (GTK_WIDGET (list));
1139 gtk_list_select_item (list, which);
1140 /* ensure_selected_item_visible (GTK_WIDGET (list)); */
1141 populate_demo_window (GTK_WIDGET (list), which, pair);
1143 updating_enabled_cb++;
1144 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled),
1145 GTK_TOGGLE_BUTTON (cb)->active);
1146 updating_enabled_cb--;
1148 /* restore the previous scroll position of the top of the list.
1149 this is weak, but I don't really know why it's moving... */
1150 gtk_adjustment_set_value (adj, scroll_top);
1154 /* Called when the right pane Enabled checkbox is clicked. This syncs
1155 the corresponding checkbox inside the scrolling list to the state
1159 enabled_cb (GtkWidget *cb, gpointer client_data)
1161 int which = selected_hack_number (cb);
1163 if (updating_enabled_cb) return;
1167 GtkList *list = GTK_LIST (name_to_widget (cb, "list"));
1168 GList *kids = GTK_LIST (list)->children;
1169 GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which));
1170 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1171 GtkWidget *line_check =
1172 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1174 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1175 GTK_TOGGLE_BUTTON (cb)->active);
1180 /* Populating the various widgets
1184 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
1187 format_time (char *buf, Time time)
1189 int s = time / 1000;
1190 unsigned int h = 0, m = 0;
1201 sprintf (buf, "%u:%02u:%02u", h, m, s);
1205 /* Finds the number of the last hack to run, and makes that item be
1206 selected by default.
1209 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
1211 saver_preferences *p = pair->a;
1214 unsigned long nitems, bytesafter;
1216 Display *dpy = gdk_display;
1220 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
1221 XA_SCREENSAVER_STATUS,
1222 0, 3, False, XA_INTEGER,
1223 &type, &format, &nitems, &bytesafter,
1224 (unsigned char **) &data)
1226 && type == XA_INTEGER
1229 which = (int) data[2] - 1;
1231 if (data) free (data);
1236 list = GTK_LIST (name_to_widget (toplevel, "list"));
1237 apply_changes_and_save (toplevel);
1238 if (which < p->screenhacks_count)
1240 gtk_list_select_item (list, which);
1241 ensure_selected_item_visible (GTK_WIDGET (list));
1242 populate_demo_window (toplevel, which, pair);
1249 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
1251 saver_preferences *p = pair->a;
1252 GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
1253 screenhack **hacks = p->screenhacks;
1256 for (h = hacks; h && *h; h++)
1258 /* A GtkList must contain only GtkListItems, but those can contain
1259 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
1260 and a Label. We handle single and double click events on the
1261 line itself, for clicking on the text, but the interior checkbox
1262 also handles its own events.
1265 GtkWidget *line_hbox;
1266 GtkWidget *line_check;
1267 GtkWidget *line_label;
1269 char *pretty_name = (h[0]->name
1270 ? strdup (h[0]->name)
1271 : make_hack_name (h[0]->command));
1273 line = gtk_list_item_new ();
1274 line_hbox = gtk_hbox_new (FALSE, 0);
1275 line_check = gtk_check_button_new ();
1276 line_label = gtk_label_new (pretty_name);
1278 gtk_container_add (GTK_CONTAINER (line), line_hbox);
1279 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
1280 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
1282 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
1284 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
1286 gtk_widget_show (line_check);
1287 gtk_widget_show (line_label);
1288 gtk_widget_show (line_hbox);
1289 gtk_widget_show (line);
1293 gtk_container_add (GTK_CONTAINER (list), line);
1294 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
1295 GTK_SIGNAL_FUNC (list_doubleclick_cb),
1298 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
1299 GTK_SIGNAL_FUNC (list_checkbox_cb),
1303 GTK_WIDGET (GTK_BIN(line)->child)->style =
1304 gtk_style_copy (GTK_WIDGET (text_line)->style);
1306 gtk_widget_show (line);
1309 gtk_signal_connect (GTK_OBJECT (list), "select_child",
1310 GTK_SIGNAL_FUNC (list_select_cb),
1312 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
1313 GTK_SIGNAL_FUNC (list_unselect_cb),
1319 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
1321 saver_preferences *p = pair->a;
1324 format_time (s, p->timeout);
1325 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
1326 format_time (s, p->cycle);
1327 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
1328 format_time (s, p->lock_timeout);
1329 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
1331 format_time (s, p->dpms_standby);
1332 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_standby_text")),s);
1333 format_time (s, p->dpms_suspend);
1334 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_suspend_text")),s);
1335 format_time (s, p->dpms_off);
1336 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_off_text")), s);
1338 format_time (s, p->fade_seconds);
1339 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
1341 gtk_toggle_button_set_active (
1342 GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
1344 gtk_toggle_button_set_active (
1345 GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
1347 gtk_toggle_button_set_active (
1348 GTK_TOGGLE_BUTTON (name_to_widget (top, "capture_button")),
1349 p->capture_stderr_p);
1350 gtk_toggle_button_set_active (
1351 GTK_TOGGLE_BUTTON (name_to_widget (top, "splash_button")),
1354 gtk_toggle_button_set_active (
1355 GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
1358 gtk_toggle_button_set_active (
1359 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1361 gtk_toggle_button_set_active (
1362 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1364 gtk_toggle_button_set_active (
1365 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1370 Bool found_any_writable_cells = False;
1371 Bool dpms_supported = False;
1373 Display *dpy = gdk_display;
1374 int nscreens = ScreenCount(dpy);
1376 for (i = 0; i < nscreens; i++)
1378 Screen *s = ScreenOfDisplay (dpy, i);
1379 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1381 found_any_writable_cells = True;
1386 #ifdef HAVE_DPMS_EXTENSION
1388 int op = 0, event = 0, error = 0;
1389 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1390 dpms_supported = True;
1392 #endif /* HAVE_DPMS_EXTENSION */
1395 /* Blanking and Locking
1397 gtk_widget_set_sensitive (
1398 GTK_WIDGET (name_to_widget (top, "lock_label")),
1400 gtk_widget_set_sensitive (
1401 GTK_WIDGET (name_to_widget (top, "lock_text")),
1406 gtk_widget_set_sensitive (
1407 GTK_WIDGET (name_to_widget (top, "dpms_frame")),
1409 gtk_widget_set_sensitive (
1410 GTK_WIDGET (name_to_widget (top, "dpms_button")),
1412 gtk_widget_set_sensitive (
1413 GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
1414 dpms_supported && p->dpms_enabled_p);
1415 gtk_widget_set_sensitive (
1416 GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
1417 dpms_supported && p->dpms_enabled_p);
1418 gtk_widget_set_sensitive (
1419 GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
1420 dpms_supported && p->dpms_enabled_p);
1421 gtk_widget_set_sensitive (
1422 GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
1423 dpms_supported && p->dpms_enabled_p);
1424 gtk_widget_set_sensitive (
1425 GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
1426 dpms_supported && p->dpms_enabled_p);
1427 gtk_widget_set_sensitive (
1428 GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
1429 dpms_supported && p->dpms_enabled_p);
1433 gtk_widget_set_sensitive (
1434 GTK_WIDGET (name_to_widget (top, "cmap_frame")),
1435 found_any_writable_cells);
1436 gtk_widget_set_sensitive (
1437 GTK_WIDGET (name_to_widget (top, "install_button")),
1438 found_any_writable_cells);
1439 gtk_widget_set_sensitive (
1440 GTK_WIDGET (name_to_widget (top, "fade_button")),
1441 found_any_writable_cells);
1442 gtk_widget_set_sensitive (
1443 GTK_WIDGET (name_to_widget (top, "unfade_button")),
1444 found_any_writable_cells);
1446 gtk_widget_set_sensitive (
1447 GTK_WIDGET (name_to_widget (top, "fade_label")),
1448 (found_any_writable_cells &&
1449 (p->fade_p || p->unfade_p)));
1450 gtk_widget_set_sensitive (
1451 GTK_WIDGET (name_to_widget (top, "fade_text")),
1452 (found_any_writable_cells &&
1453 (p->fade_p || p->unfade_p)));
1460 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1462 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1463 "visual", "visual_combo",
1466 for (i = 0; i < countof(names); i++)
1468 GtkWidget *w = name_to_widget (toplevel, names[i]);
1469 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1472 /* I don't know how to handle these yet... */
1474 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1475 for (i = 0; i < countof(names2); i++)
1477 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1478 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1484 /* Even though we've given these text fields a maximum number of characters,
1485 their default size is still about 30 characters wide -- so measure out
1486 a string in their font, and resize them to just fit that.
1489 fix_text_entry_sizes (GtkWidget *toplevel)
1491 const char *names[] = { "timeout_text", "cycle_text", "lock_text",
1492 "dpms_standby_text", "dpms_suspend_text",
1493 "dpms_off_text", "fade_text" };
1498 for (i = 0; i < countof(names); i++)
1500 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1502 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1503 gtk_widget_set_usize (w, width, -2);
1506 /* Now fix the size of the combo box.
1508 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1509 w = GTK_COMBO (w)->entry;
1510 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1511 gtk_widget_set_usize (w, width, -2);
1514 /* Now fix the size of the list.
1516 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1517 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1518 gtk_widget_set_usize (w, width, -2);
1525 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1528 static char *up_arrow_xpm[] = {
1551 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1552 the end of the array (Gtk 1.2.5.) */
1553 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1554 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1557 static char *down_arrow_xpm[] = {
1580 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1581 the end of the array (Gtk 1.2.5.) */
1582 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1583 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1587 pixmapify_button (GtkWidget *toplevel, int down_p)
1591 GtkWidget *pixmapwid;
1595 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1596 (down_p ? "next" : "prev")));
1597 style = gtk_widget_get_style (w);
1599 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1600 &style->bg[GTK_STATE_NORMAL],
1602 ? (gchar **) down_arrow_xpm
1603 : (gchar **) up_arrow_xpm));
1604 pixmapwid = gtk_pixmap_new (pixmap, mask);
1605 gtk_widget_show (pixmapwid);
1606 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1607 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1611 map_next_button_cb (GtkWidget *w, gpointer user_data)
1613 pixmapify_button (w, 1);
1617 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1619 pixmapify_button (w, 0);
1624 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1628 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1629 GtkAllocation *allocation,
1633 GtkWidgetAuxInfo *aux_info;
1635 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1637 aux_info->width = allocation->width;
1638 aux_info->height = -2;
1642 gtk_widget_size_request (label, &req);
1646 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1649 eschew_gtk_lossage (GtkWidget *toplevel)
1651 GtkWidgetAuxInfo *aux_info;
1652 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1654 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1655 aux_info->width = label->allocation.width;
1656 aux_info->height = -2;
1660 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1662 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1663 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1665 gtk_widget_queue_resize (label);
1670 get_hack_blurb (screenhack *hack)
1673 char *prog_name = strdup (hack->command);
1674 char *pretty_name = (hack->name
1675 ? strdup (hack->name)
1676 : make_hack_name (hack->command));
1677 char doc_name[255], doc_class[255];
1680 for (s = prog_name; *s && !isspace(*s); s++)
1683 s = strrchr (prog_name, '/');
1684 if (s) strcpy (prog_name, s+1);
1686 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1687 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1691 doc_string = get_string_resource (doc_name, doc_class);
1694 for (s = doc_string; *s; s++)
1698 /* skip over whitespace at beginning of line */
1700 while (*s && (*s == ' ' || *s == '\t'))
1703 else if (*s == ' ' || *s == '\t')
1705 /* compress all other horizontal whitespace. */
1708 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1710 if (s2 > s) strcpy (s, s2);
1715 while (*s && isspace (*s)) /* Strip trailing whitespace */
1718 /* Delete whitespace at end of each line. */
1719 for (; s > doc_string; s--)
1720 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1723 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1727 if (s2 < s) strcpy (s2, s);
1731 /* Delete leading blank lines. */
1732 for (s = doc_string; *s == '\n'; s++)
1734 if (s > doc_string) strcpy (doc_string, s);
1738 static int doc_installed = 0;
1739 if (doc_installed == 0)
1741 if (get_boolean_resource ("hacks.documentation.isInstalled",
1742 "hacks.documentation.isInstalled"))
1748 if (doc_installed < 0)
1750 strdup ("Error:\n\n"
1751 "The documentation strings do not appear to be "
1752 "installed. This is probably because there is "
1753 "an \"XScreenSaver\" app-defaults file installed "
1754 "that is from an older version of the program. "
1755 "To fix this problem, delete that file, or "
1756 "install a current version (either will work.)");
1758 doc_string = strdup ("");
1766 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1768 saver_preferences *p = pair->a;
1769 screenhack *hack = (which >= 0 && which < p->screenhacks_count
1770 ? p->screenhacks[which] : 0);
1771 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1772 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1773 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1774 GtkToggleButton *enabled =
1775 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1776 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1778 char *pretty_name = (hack
1780 ? strdup (hack->name)
1781 : make_hack_name (hack->command))
1783 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1785 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1786 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1787 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1788 gtk_entry_set_position (cmd, 0);
1790 updating_enabled_cb++;
1791 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1792 updating_enabled_cb--;
1794 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1796 ? (hack->visual && *hack->visual
1801 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1803 sensitize_demo_widgets (toplevel, (hack ? True : False));
1805 if (pretty_name) free (pretty_name);
1806 if (doc_string) free (doc_string);
1808 _selected_hack_number = which;
1813 widget_deleter (GtkWidget *widget, gpointer data)
1815 /* #### Well, I want to destroy these widgets, but if I do that, they get
1816 referenced again, and eventually I get a SEGV. So instead of
1817 destroying them, I'll just hide them, and leak a bunch of memory
1818 every time the disk file changes. Go go go Gtk!
1820 #### Ok, that's a lie, I get a crash even if I just hide the widget
1821 and don't ever delete it. Fuck!
1824 gtk_widget_destroy (widget);
1826 gtk_widget_hide (widget);
1832 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1835 saver_preferences *p = pair->a;
1837 static Bool reentrant_lock = False;
1838 if (reentrant_lock) return 0;
1839 reentrant_lock = True;
1841 if (init_file_changed_p (p))
1843 const char *f = init_file_name();
1848 if (!f || !*f) return 0;
1849 b = (char *) malloc (strlen(f) + 1024);
1852 "file \"%s\" has changed, reloading.\n",
1854 warning_dialog (widget, b, False, 100);
1859 which = selected_hack_number (widget);
1860 list = GTK_LIST (name_to_widget (widget, "list"));
1861 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1862 populate_hack_list (widget, pair);
1863 gtk_list_select_item (list, which);
1864 populate_prefs_page (widget, pair);
1865 populate_demo_window (widget, which, pair);
1866 ensure_selected_item_visible (GTK_WIDGET (list));
1871 reentrant_lock = False;
1877 /* Setting window manager icon
1881 init_icon (GdkWindow *window)
1883 GdkBitmap *mask = 0;
1886 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
1887 (gchar **) logo_50_xpm);
1889 gdk_window_set_icon (window, 0, pixmap, mask);
1893 /* The main demo-mode command loop.
1898 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1899 XrmRepresentation *type, XrmValue *value, XPointer closure)
1902 for (i = 0; quarks[i]; i++)
1904 if (bindings[i] == XrmBindTightly)
1905 fprintf (stderr, (i == 0 ? "" : "."));
1906 else if (bindings[i] == XrmBindLoosely)
1907 fprintf (stderr, "*");
1909 fprintf (stderr, " ??? ");
1910 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1913 fprintf (stderr, ": %s\n", (char *) value->addr);
1921 the_network_is_not_the_computer (GtkWidget *parent)
1923 Display *dpy = gdk_display;
1924 char *rversion, *ruser, *rhost;
1925 char *luser, *lhost;
1927 struct passwd *p = getpwuid (getuid ());
1928 const char *d = DisplayString (dpy);
1930 # if defined(HAVE_UNAME)
1932 if (uname (&uts) < 0)
1933 lhost = "<UNKNOWN>";
1935 lhost = uts.nodename;
1937 strcpy (lhost, getenv("SYS$NODE"));
1938 # else /* !HAVE_UNAME && !VMS */
1939 strcat (lhost, "<UNKNOWN>");
1940 # endif /* !HAVE_UNAME && !VMS */
1942 if (p && p->pw_name)
1947 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1949 /* Make a buffer that's big enough for a number of copies of all the
1950 strings, plus some. */
1951 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1952 (ruser ? strlen(ruser) : 0) +
1953 (rhost ? strlen(rhost) : 0) +
1960 if (!rversion || !*rversion)
1964 "The XScreenSaver daemon doesn't seem to be running\n"
1965 "on display \"%s\". Launch it now?",
1968 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1970 /* Warn that the two processes are running as different users.
1974 "%s is running as user \"%s\" on host \"%s\".\n"
1975 "But the xscreensaver managing display \"%s\"\n"
1976 "is running as user \"%s\" on host \"%s\".\n"
1978 "Since they are different users, they won't be reading/writing\n"
1979 "the same ~/.xscreensaver file, so %s isn't\n"
1980 "going to work right.\n"
1982 "You should either re-run %s as \"%s\", or re-run\n"
1983 "xscreensaver as \"%s\".\n"
1985 "Restart the xscreensaver daemon now?\n",
1986 progname, luser, lhost,
1988 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1990 progname, (ruser ? ruser : "???"),
1993 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1995 /* Warn that the two processes are running on different hosts.
1999 "%s is running as user \"%s\" on host \"%s\".\n"
2000 "But the xscreensaver managing display \"%s\"\n"
2001 "is running as user \"%s\" on host \"%s\".\n"
2003 "If those two machines don't share a file system (that is,\n"
2004 "if they don't see the same ~%s/.xscreensaver file) then\n"
2005 "%s won't work right.\n"
2007 "Restart the daemon on \"%s\" as \"%s\" now?\n",
2008 progname, luser, lhost,
2010 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
2015 else if (!!strcmp (rversion, short_version))
2017 /* Warn that the version numbers don't match.
2021 "This is %s version %s.\n"
2022 "But the xscreensaver managing display \"%s\"\n"
2023 "is version %s. This could cause problems.\n"
2025 "Restart the xscreensaver daemon now?\n",
2026 progname, short_version,
2033 warning_dialog (parent, msg, True, 1);
2039 /* We use this error handler so that X errors are preceeded by the name
2040 of the program that generated them.
2043 demo_ehandler (Display *dpy, XErrorEvent *error)
2045 fprintf (stderr, "\nX error in %s:\n", progname);
2046 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
2049 fprintf (stderr, " (nonfatal.)\n");
2054 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
2055 of the program that generated them; and also that we can ignore one
2056 particular bogus error message that Gdk madly spews.
2059 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
2060 const gchar *message, gpointer user_data)
2062 /* Ignore the message "Got event for unknown window: 0x...".
2063 Apparently some events are coming in for the xscreensaver window
2064 (presumably reply events related to the ClientMessage) and Gdk
2065 feels the need to complain about them. So, just suppress any
2066 messages that look like that one.
2068 if (strstr (message, "unknown window"))
2071 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
2072 (log_level == G_LOG_LEVEL_ERROR ? "error" :
2073 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
2074 log_level == G_LOG_LEVEL_WARNING ? "warning" :
2075 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
2076 log_level == G_LOG_LEVEL_INFO ? "info" :
2077 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
2079 ((!*message || message[strlen(message)-1] != '\n')
2084 static char *defaults[] = {
2085 #include "XScreenSaver_ad.h"
2090 #ifdef HAVE_CRAPPLET
2091 static struct poptOption crapplet_options[] = {
2092 {NULL, '\0', 0, NULL, 0}
2094 #endif /* HAVE_CRAPPLET */
2098 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
2103 map_window_cb (GtkWidget *w, gpointer user_data)
2105 Boolean oi = initializing_p;
2106 initializing_p = True;
2107 eschew_gtk_lossage (w);
2108 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
2109 initializing_p = oi;
2114 main (int argc, char **argv)
2117 prefs_pair Pair, *pair;
2118 saver_preferences P, P2, *p, *p2;
2122 Widget toplevel_shell;
2123 GtkWidget *gtk_window;
2124 char *real_progname = argv[0];
2127 initializing_p = True;
2129 s = strrchr (real_progname, '/');
2130 if (s) real_progname = s+1;
2137 memset (p, 0, sizeof (*p));
2138 memset (p2, 0, sizeof (*p2));
2140 global_prefs_pair = pair; /* I hate C so much... */
2142 progname = real_progname;
2144 short_version = (char *) malloc (5);
2145 memcpy (short_version, screensaver_id + 17, 4);
2146 short_version [4] = 0;
2149 /* Register our error message logger for every ``log domain'' known.
2150 There's no way to do this globally, so I grepped the Gtk/Gdk sources
2151 for all of the domains that seem to be in use.
2154 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
2155 "GThread", "Gnome", "GnomeUI", 0 };
2156 for (i = 0; domains[i]; i++)
2157 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
2160 /* This is gross, but Gtk understands --display and not -display...
2162 for (i = 1; i < argc; i++)
2163 if (argv[i][0] && argv[i][1] &&
2164 !strncmp(argv[i], "-display", strlen(argv[i])))
2165 argv[i] = "--display";
2168 /* We need to parse this arg really early... Sigh. */
2169 for (i = 1; i < argc; i++)
2171 (!strcmp(argv[i], "--crapplet") ||
2172 !strcmp(argv[i], "--capplet")))
2174 # ifdef HAVE_CRAPPLET
2177 for (j = i; j < argc; j++) /* remove it from the list */
2178 argv[j] = argv[j+1];
2181 # else /* !HAVE_CRAPPLET */
2182 fprintf (stderr, "%s: not compiled with --crapplet support\n",
2186 # endif /* !HAVE_CRAPPLET */
2189 /* Let Gtk open the X connection, then initialize Xt to use that
2190 same connection. Doctor Frankenstein would be proud.
2192 # ifdef HAVE_CRAPPLET
2195 GnomeClient *client;
2196 GnomeClientFlags flags = 0;
2198 int init_results = gnome_capplet_init ("screensaver-properties",
2200 argc, argv, NULL, 0, NULL);
2202 0 upon successful initialization;
2203 1 if --init-session-settings was passed on the cmdline;
2204 2 if --ignore was passed on the cmdline;
2207 So the 1 signifies just to init the settings, and quit, basically.
2208 (Meaning launch the xscreensaver daemon.)
2211 if (init_results < 0)
2214 g_error ("An initialization error occurred while "
2215 "starting xscreensaver-capplet.\n");
2217 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
2218 real_progname, init_results);
2223 client = gnome_master_client ();
2226 flags = gnome_client_get_flags (client);
2228 if (flags & GNOME_CLIENT_IS_CONNECTED)
2231 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2232 gnome_client_get_id (client));
2235 char *session_args[20];
2237 session_args[i++] = real_progname;
2238 session_args[i++] = "--capplet";
2239 session_args[i++] = "--init-session-settings";
2240 session_args[i] = 0;
2241 gnome_client_set_priority (client, 20);
2242 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2243 gnome_client_set_restart_command (client, i, session_args);
2247 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2250 gnome_client_flush (client);
2253 if (init_results == 1)
2255 system ("xscreensaver -nosplash &");
2261 # endif /* HAVE_CRAPPLET */
2263 gtk_init (&argc, &argv);
2267 /* We must read exactly the same resources as xscreensaver.
2268 That means we must have both the same progclass *and* progname,
2269 at least as far as the resource database is concerned. So,
2270 put "xscreensaver" in argv[0] while initializing Xt.
2272 argv[0] = "xscreensaver";
2276 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2278 XtToolkitInitialize ();
2279 app = XtCreateApplicationContext ();
2281 XtAppSetFallbackResources (app, defaults);
2282 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2283 toplevel_shell = XtAppCreateShell (progname, progclass,
2284 applicationShellWidgetClass,
2287 dpy = XtDisplay (toplevel_shell);
2288 db = XtDatabase (dpy);
2289 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2290 XSetErrorHandler (demo_ehandler);
2293 /* After doing Xt-style command-line processing, complain about any
2294 unrecognized command-line arguments.
2296 for (i = 1; i < argc; i++)
2299 if (s[0] == '-' && s[1] == '-')
2301 if (!strcmp (s, "-prefs"))
2303 else if (crapplet_p)
2304 /* There are lots of random args that we don't care about when we're
2305 started as a crapplet, so just ignore unknown args in that case. */
2309 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2315 /* Load the init file, which may end up consulting the X resource database
2316 and the site-wide app-defaults file. Note that at this point, it's
2317 important that `progname' be "xscreensaver", rather than whatever
2324 /* Now that Xt has been initialized, and the resources have been read,
2325 we can set our `progname' variable to something more in line with
2328 progname = real_progname;
2332 /* Print out all the resources we read. */
2334 XrmName name = { 0 };
2335 XrmClass class = { 0 };
2337 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2343 /* Intern the atoms that xscreensaver_command() needs.
2345 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2346 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2347 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2348 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2349 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2350 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2351 XA_SELECT = XInternAtom (dpy, "SELECT", False);
2352 XA_DEMO = XInternAtom (dpy, "DEMO", False);
2353 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2354 XA_BLANK = XInternAtom (dpy, "BLANK", False);
2355 XA_LOCK = XInternAtom (dpy, "LOCK", False);
2356 XA_EXIT = XInternAtom (dpy, "EXIT", False);
2357 XA_RESTART = XInternAtom (dpy, "RESTART", False);
2360 /* Create the window and all its widgets.
2362 gtk_window = create_xscreensaver_demo ();
2363 toplevel_widget = gtk_window;
2365 /* Set the window's title. */
2368 char *v = (char *) strdup(strchr(screensaver_id, ' '));
2369 char *s1, *s2, *s3, *s4;
2370 s1 = (char *) strchr(v, ' '); s1++;
2371 s2 = (char *) strchr(s1, ' ');
2372 s3 = (char *) strchr(v, '('); s3++;
2373 s4 = (char *) strchr(s3, ')');
2376 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2377 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2381 /* Various other widget initializations...
2383 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2384 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2386 populate_hack_list (gtk_window, pair);
2387 populate_prefs_page (gtk_window, pair);
2388 sensitize_demo_widgets (gtk_window, False);
2389 fix_text_entry_sizes (gtk_window);
2390 scroll_to_current_hack (gtk_window, pair);
2392 gtk_signal_connect (
2393 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2394 "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2395 gtk_signal_connect (
2396 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2397 "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2398 gtk_signal_connect (
2399 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2400 "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2403 /* Handle the -prefs command-line argument. */
2406 GtkNotebook *notebook =
2407 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2408 gtk_notebook_set_page (notebook, 1);
2411 # ifdef HAVE_CRAPPLET
2415 GtkWidget *top_vbox;
2417 capplet = capplet_widget_new ();
2419 top_vbox = GTK_BIN (gtk_window)->child;
2421 gtk_widget_ref (top_vbox);
2422 gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2423 GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2425 /* In crapplet-mode, take off the menubar. */
2426 gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2428 gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2429 gtk_widget_show (capplet);
2430 gtk_widget_hide (gtk_window);
2432 /* Hook up the Control Center's redundant Help button, too. */
2433 gtk_signal_connect (GTK_OBJECT (capplet), "help",
2434 GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2436 /* Issue any warnings about the running xscreensaver daemon. */
2437 the_network_is_not_the_computer (top_vbox);
2440 # endif /* HAVE_CRAPPLET */
2442 gtk_widget_show (gtk_window);
2443 init_icon (GTK_WIDGET(gtk_window)->window);
2445 /* Issue any warnings about the running xscreensaver daemon. */
2446 the_network_is_not_the_computer (gtk_window);
2449 /* Run the Gtk event loop, and not the Xt event loop. This means that
2450 if there were Xt timers or fds registered, they would never get serviced,
2451 and if there were any Xt widgets, they would never have events delivered.
2452 Fortunately, we're using Gtk for all of the UI, and only initialized
2453 Xt so that we could process the command line and use the X resource
2456 initializing_p = False;
2458 # ifdef HAVE_CRAPPLET
2460 capplet_gtk_main ();
2462 # endif /* HAVE_CRAPPLET */
2468 #endif /* HAVE_GTK -- whole file */