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);
1351 gtk_toggle_button_set_active (
1352 GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
1355 gtk_toggle_button_set_active (
1356 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
1358 gtk_toggle_button_set_active (
1359 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
1361 gtk_toggle_button_set_active (
1362 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
1367 Bool found_any_writable_cells = False;
1368 Bool dpms_supported = False;
1370 Display *dpy = gdk_display;
1371 int nscreens = ScreenCount(dpy);
1373 for (i = 0; i < nscreens; i++)
1375 Screen *s = ScreenOfDisplay (dpy, i);
1376 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
1378 found_any_writable_cells = True;
1383 #ifdef HAVE_DPMS_EXTENSION
1385 int op = 0, event = 0, error = 0;
1386 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
1387 dpms_supported = True;
1389 #endif /* HAVE_DPMS_EXTENSION */
1392 /* Blanking and Locking
1394 gtk_widget_set_sensitive (
1395 GTK_WIDGET (name_to_widget (top, "lock_label")),
1397 gtk_widget_set_sensitive (
1398 GTK_WIDGET (name_to_widget (top, "lock_text")),
1403 gtk_widget_set_sensitive (
1404 GTK_WIDGET (name_to_widget (top, "dpms_frame")),
1406 gtk_widget_set_sensitive (
1407 GTK_WIDGET (name_to_widget (top, "dpms_button")),
1409 gtk_widget_set_sensitive (
1410 GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
1411 dpms_supported && p->dpms_enabled_p);
1412 gtk_widget_set_sensitive (
1413 GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
1414 dpms_supported && p->dpms_enabled_p);
1415 gtk_widget_set_sensitive (
1416 GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
1417 dpms_supported && p->dpms_enabled_p);
1418 gtk_widget_set_sensitive (
1419 GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
1420 dpms_supported && p->dpms_enabled_p);
1421 gtk_widget_set_sensitive (
1422 GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
1423 dpms_supported && p->dpms_enabled_p);
1424 gtk_widget_set_sensitive (
1425 GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
1426 dpms_supported && p->dpms_enabled_p);
1430 gtk_widget_set_sensitive (
1431 GTK_WIDGET (name_to_widget (top, "cmap_frame")),
1432 found_any_writable_cells);
1433 gtk_widget_set_sensitive (
1434 GTK_WIDGET (name_to_widget (top, "install_button")),
1435 found_any_writable_cells);
1436 gtk_widget_set_sensitive (
1437 GTK_WIDGET (name_to_widget (top, "fade_button")),
1438 found_any_writable_cells);
1439 gtk_widget_set_sensitive (
1440 GTK_WIDGET (name_to_widget (top, "unfade_button")),
1441 found_any_writable_cells);
1443 gtk_widget_set_sensitive (
1444 GTK_WIDGET (name_to_widget (top, "fade_label")),
1445 (found_any_writable_cells &&
1446 (p->fade_p || p->unfade_p)));
1447 gtk_widget_set_sensitive (
1448 GTK_WIDGET (name_to_widget (top, "fade_text")),
1449 (found_any_writable_cells &&
1450 (p->fade_p || p->unfade_p)));
1457 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1459 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1460 "visual", "visual_combo",
1463 for (i = 0; i < countof(names); i++)
1465 GtkWidget *w = name_to_widget (toplevel, names[i]);
1466 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1469 /* I don't know how to handle these yet... */
1471 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1472 for (i = 0; i < countof(names2); i++)
1474 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1475 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1481 /* Even though we've given these text fields a maximum number of characters,
1482 their default size is still about 30 characters wide -- so measure out
1483 a string in their font, and resize them to just fit that.
1486 fix_text_entry_sizes (GtkWidget *toplevel)
1488 const char *names[] = { "timeout_text", "cycle_text", "lock_text",
1489 "dpms_standby_text", "dpms_suspend_text",
1490 "dpms_off_text", "fade_text" };
1495 for (i = 0; i < countof(names); i++)
1497 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1499 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1500 gtk_widget_set_usize (w, width, -2);
1503 /* Now fix the size of the combo box.
1505 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1506 w = GTK_COMBO (w)->entry;
1507 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1508 gtk_widget_set_usize (w, width, -2);
1511 /* Now fix the size of the list.
1513 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1514 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1515 gtk_widget_set_usize (w, width, -2);
1522 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1525 static char *up_arrow_xpm[] = {
1548 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1549 the end of the array (Gtk 1.2.5.) */
1550 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1551 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1554 static char *down_arrow_xpm[] = {
1577 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1578 the end of the array (Gtk 1.2.5.) */
1579 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1580 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1584 pixmapify_button (GtkWidget *toplevel, int down_p)
1588 GtkWidget *pixmapwid;
1592 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
1593 (down_p ? "next" : "prev")));
1594 style = gtk_widget_get_style (w);
1596 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1597 &style->bg[GTK_STATE_NORMAL],
1599 ? (gchar **) down_arrow_xpm
1600 : (gchar **) up_arrow_xpm));
1601 pixmapwid = gtk_pixmap_new (pixmap, mask);
1602 gtk_widget_show (pixmapwid);
1603 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1604 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1608 map_next_button_cb (GtkWidget *w, gpointer user_data)
1610 pixmapify_button (w, 1);
1614 map_prev_button_cb (GtkWidget *w, gpointer user_data)
1616 pixmapify_button (w, 0);
1621 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1625 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1626 GtkAllocation *allocation,
1630 GtkWidgetAuxInfo *aux_info;
1632 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1634 aux_info->width = allocation->width;
1635 aux_info->height = -2;
1639 gtk_widget_size_request (label, &req);
1643 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1646 eschew_gtk_lossage (GtkWidget *toplevel)
1648 GtkWidgetAuxInfo *aux_info;
1649 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1651 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1652 aux_info->width = label->allocation.width;
1653 aux_info->height = -2;
1657 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1659 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1660 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1662 gtk_widget_queue_resize (label);
1667 get_hack_blurb (screenhack *hack)
1670 char *prog_name = strdup (hack->command);
1671 char *pretty_name = (hack->name
1672 ? strdup (hack->name)
1673 : make_hack_name (hack->command));
1674 char doc_name[255], doc_class[255];
1677 for (s = prog_name; *s && !isspace(*s); s++)
1680 s = strrchr (prog_name, '/');
1681 if (s) strcpy (prog_name, s+1);
1683 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1684 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1688 doc_string = get_string_resource (doc_name, doc_class);
1691 for (s = doc_string; *s; s++)
1695 /* skip over whitespace at beginning of line */
1697 while (*s && (*s == ' ' || *s == '\t'))
1700 else if (*s == ' ' || *s == '\t')
1702 /* compress all other horizontal whitespace. */
1705 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1707 if (s2 > s) strcpy (s, s2);
1712 while (*s && isspace (*s)) /* Strip trailing whitespace */
1715 /* Delete whitespace at end of each line. */
1716 for (; s > doc_string; s--)
1717 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1720 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1724 if (s2 < s) strcpy (s2, s);
1728 /* Delete leading blank lines. */
1729 for (s = doc_string; *s == '\n'; s++)
1731 if (s > doc_string) strcpy (doc_string, s);
1735 static int doc_installed = 0;
1736 if (doc_installed == 0)
1738 if (get_boolean_resource ("hacks.documentation.isInstalled",
1739 "hacks.documentation.isInstalled"))
1745 if (doc_installed < 0)
1747 strdup ("Error:\n\n"
1748 "The documentation strings do not appear to be "
1749 "installed. This is probably because there is "
1750 "an \"XScreenSaver\" app-defaults file installed "
1751 "that is from an older version of the program. "
1752 "To fix this problem, delete that file, or "
1753 "install a current version (either will work.)");
1755 doc_string = strdup ("");
1763 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1765 saver_preferences *p = pair->a;
1766 screenhack *hack = (which >= 0 && which < p->screenhacks_count
1767 ? p->screenhacks[which] : 0);
1768 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1769 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1770 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1771 GtkToggleButton *enabled =
1772 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1773 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1775 char *pretty_name = (hack
1777 ? strdup (hack->name)
1778 : make_hack_name (hack->command))
1780 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1782 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1783 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1784 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1785 gtk_entry_set_position (cmd, 0);
1787 updating_enabled_cb++;
1788 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1789 updating_enabled_cb--;
1791 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1793 ? (hack->visual && *hack->visual
1798 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1800 sensitize_demo_widgets (toplevel, (hack ? True : False));
1802 if (pretty_name) free (pretty_name);
1803 if (doc_string) free (doc_string);
1805 _selected_hack_number = which;
1810 widget_deleter (GtkWidget *widget, gpointer data)
1812 /* #### Well, I want to destroy these widgets, but if I do that, they get
1813 referenced again, and eventually I get a SEGV. So instead of
1814 destroying them, I'll just hide them, and leak a bunch of memory
1815 every time the disk file changes. Go go go Gtk!
1817 #### Ok, that's a lie, I get a crash even if I just hide the widget
1818 and don't ever delete it. Fuck!
1821 gtk_widget_destroy (widget);
1823 gtk_widget_hide (widget);
1829 maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
1832 saver_preferences *p = pair->a;
1834 static Bool reentrant_lock = False;
1835 if (reentrant_lock) return 0;
1836 reentrant_lock = True;
1838 if (init_file_changed_p (p))
1840 const char *f = init_file_name();
1845 if (!f || !*f) return 0;
1846 b = (char *) malloc (strlen(f) + 1024);
1849 "file \"%s\" has changed, reloading.\n",
1851 warning_dialog (widget, b, False, 100);
1856 which = selected_hack_number (widget);
1857 list = GTK_LIST (name_to_widget (widget, "list"));
1858 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
1859 populate_hack_list (widget, pair);
1860 gtk_list_select_item (list, which);
1861 populate_prefs_page (widget, pair);
1862 populate_demo_window (widget, which, pair);
1863 ensure_selected_item_visible (GTK_WIDGET (list));
1868 reentrant_lock = False;
1874 /* Setting window manager icon
1878 init_icon (GdkWindow *window)
1880 GdkBitmap *mask = 0;
1883 gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
1884 (gchar **) logo_50_xpm);
1886 gdk_window_set_icon (window, 0, pixmap, mask);
1890 /* The main demo-mode command loop.
1895 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1896 XrmRepresentation *type, XrmValue *value, XPointer closure)
1899 for (i = 0; quarks[i]; i++)
1901 if (bindings[i] == XrmBindTightly)
1902 fprintf (stderr, (i == 0 ? "" : "."));
1903 else if (bindings[i] == XrmBindLoosely)
1904 fprintf (stderr, "*");
1906 fprintf (stderr, " ??? ");
1907 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1910 fprintf (stderr, ": %s\n", (char *) value->addr);
1918 the_network_is_not_the_computer (GtkWidget *parent)
1920 Display *dpy = gdk_display;
1921 char *rversion, *ruser, *rhost;
1922 char *luser, *lhost;
1924 struct passwd *p = getpwuid (getuid ());
1925 const char *d = DisplayString (dpy);
1927 # if defined(HAVE_UNAME)
1929 if (uname (&uts) < 0)
1930 lhost = "<UNKNOWN>";
1932 lhost = uts.nodename;
1934 strcpy (lhost, getenv("SYS$NODE"));
1935 # else /* !HAVE_UNAME && !VMS */
1936 strcat (lhost, "<UNKNOWN>");
1937 # endif /* !HAVE_UNAME && !VMS */
1939 if (p && p->pw_name)
1944 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1946 /* Make a buffer that's big enough for a number of copies of all the
1947 strings, plus some. */
1948 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1949 (ruser ? strlen(ruser) : 0) +
1950 (rhost ? strlen(rhost) : 0) +
1957 if (!rversion || !*rversion)
1961 "The XScreenSaver daemon doesn't seem to be running\n"
1962 "on display \"%s\". Launch it now?",
1965 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1967 /* Warn that the two processes are running as different users.
1971 "%s is running as user \"%s\" on host \"%s\".\n"
1972 "But the xscreensaver managing display \"%s\"\n"
1973 "is running as user \"%s\" on host \"%s\".\n"
1975 "Since they are different users, they won't be reading/writing\n"
1976 "the same ~/.xscreensaver file, so %s isn't\n"
1977 "going to work right.\n"
1979 "You should either re-run %s as \"%s\", or re-run\n"
1980 "xscreensaver as \"%s\".\n"
1982 "Restart the xscreensaver daemon now?\n",
1983 progname, luser, lhost,
1985 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1987 progname, (ruser ? ruser : "???"),
1990 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1992 /* Warn that the two processes are running on different hosts.
1996 "%s is running as user \"%s\" on host \"%s\".\n"
1997 "But the xscreensaver managing display \"%s\"\n"
1998 "is running as user \"%s\" on host \"%s\".\n"
2000 "If those two machines don't share a file system (that is,\n"
2001 "if they don't see the same ~%s/.xscreensaver file) then\n"
2002 "%s won't work right.\n"
2004 "Restart the daemon on \"%s\" as \"%s\" now?\n",
2005 progname, luser, lhost,
2007 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
2012 else if (!!strcmp (rversion, short_version))
2014 /* Warn that the version numbers don't match.
2018 "This is %s version %s.\n"
2019 "But the xscreensaver managing display \"%s\"\n"
2020 "is version %s. This could cause problems.\n"
2022 "Restart the xscreensaver daemon now?\n",
2023 progname, short_version,
2030 warning_dialog (parent, msg, True, 1);
2036 /* We use this error handler so that X errors are preceeded by the name
2037 of the program that generated them.
2040 demo_ehandler (Display *dpy, XErrorEvent *error)
2042 fprintf (stderr, "\nX error in %s:\n", progname);
2043 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
2046 fprintf (stderr, " (nonfatal.)\n");
2051 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
2052 of the program that generated them; and also that we can ignore one
2053 particular bogus error message that Gdk madly spews.
2056 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
2057 const gchar *message, gpointer user_data)
2059 /* Ignore the message "Got event for unknown window: 0x...".
2060 Apparently some events are coming in for the xscreensaver window
2061 (presumably reply events related to the ClientMessage) and Gdk
2062 feels the need to complain about them. So, just suppress any
2063 messages that look like that one.
2065 if (strstr (message, "unknown window"))
2068 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
2069 (log_level == G_LOG_LEVEL_ERROR ? "error" :
2070 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
2071 log_level == G_LOG_LEVEL_WARNING ? "warning" :
2072 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
2073 log_level == G_LOG_LEVEL_INFO ? "info" :
2074 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
2076 ((!*message || message[strlen(message)-1] != '\n')
2081 static char *defaults[] = {
2082 #include "XScreenSaver_ad.h"
2087 #ifdef HAVE_CRAPPLET
2088 static struct poptOption crapplet_options[] = {
2089 {NULL, '\0', 0, NULL, 0}
2091 #endif /* HAVE_CRAPPLET */
2095 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
2100 map_window_cb (GtkWidget *w, gpointer user_data)
2102 Boolean oi = initializing_p;
2103 initializing_p = True;
2104 eschew_gtk_lossage (w);
2105 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list")));
2106 initializing_p = oi;
2111 main (int argc, char **argv)
2114 prefs_pair Pair, *pair;
2115 saver_preferences P, P2, *p, *p2;
2119 Widget toplevel_shell;
2120 GtkWidget *gtk_window;
2121 char *real_progname = argv[0];
2124 initializing_p = True;
2126 s = strrchr (real_progname, '/');
2127 if (s) real_progname = s+1;
2134 memset (p, 0, sizeof (*p));
2135 memset (p2, 0, sizeof (*p2));
2137 global_prefs_pair = pair; /* I hate C so much... */
2139 progname = real_progname;
2141 short_version = (char *) malloc (5);
2142 memcpy (short_version, screensaver_id + 17, 4);
2143 short_version [4] = 0;
2146 /* Register our error message logger for every ``log domain'' known.
2147 There's no way to do this globally, so I grepped the Gtk/Gdk sources
2148 for all of the domains that seem to be in use.
2151 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
2152 "GThread", "Gnome", "GnomeUI", 0 };
2153 for (i = 0; domains[i]; i++)
2154 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
2157 /* This is gross, but Gtk understands --display and not -display...
2159 for (i = 1; i < argc; i++)
2160 if (argv[i][0] && argv[i][1] &&
2161 !strncmp(argv[i], "-display", strlen(argv[i])))
2162 argv[i] = "--display";
2165 /* We need to parse this arg really early... Sigh. */
2166 for (i = 1; i < argc; i++)
2168 (!strcmp(argv[i], "--crapplet") ||
2169 !strcmp(argv[i], "--capplet")))
2171 # ifdef HAVE_CRAPPLET
2174 for (j = i; j < argc; j++) /* remove it from the list */
2175 argv[j] = argv[j+1];
2178 # else /* !HAVE_CRAPPLET */
2179 fprintf (stderr, "%s: not compiled with --crapplet support\n",
2183 # endif /* !HAVE_CRAPPLET */
2186 /* Let Gtk open the X connection, then initialize Xt to use that
2187 same connection. Doctor Frankenstein would be proud.
2189 # ifdef HAVE_CRAPPLET
2192 GnomeClient *client;
2193 GnomeClientFlags flags = 0;
2195 int init_results = gnome_capplet_init ("screensaver-properties",
2197 argc, argv, NULL, 0, NULL);
2199 0 upon successful initialization;
2200 1 if --init-session-settings was passed on the cmdline;
2201 2 if --ignore was passed on the cmdline;
2204 So the 1 signifies just to init the settings, and quit, basically.
2205 (Meaning launch the xscreensaver daemon.)
2208 if (init_results < 0)
2211 g_error ("An initialization error occurred while "
2212 "starting xscreensaver-capplet.\n");
2214 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
2215 real_progname, init_results);
2220 client = gnome_master_client ();
2223 flags = gnome_client_get_flags (client);
2225 if (flags & GNOME_CLIENT_IS_CONNECTED)
2228 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
2229 gnome_client_get_id (client));
2232 char *session_args[20];
2234 session_args[i++] = real_progname;
2235 session_args[i++] = "--capplet";
2236 session_args[i++] = "--init-session-settings";
2237 session_args[i] = 0;
2238 gnome_client_set_priority (client, 20);
2239 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
2240 gnome_client_set_restart_command (client, i, session_args);
2244 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
2247 gnome_client_flush (client);
2250 if (init_results == 1)
2252 system ("xscreensaver -nosplash &");
2258 # endif /* HAVE_CRAPPLET */
2260 gtk_init (&argc, &argv);
2264 /* We must read exactly the same resources as xscreensaver.
2265 That means we must have both the same progclass *and* progname,
2266 at least as far as the resource database is concerned. So,
2267 put "xscreensaver" in argv[0] while initializing Xt.
2269 argv[0] = "xscreensaver";
2273 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
2275 XtToolkitInitialize ();
2276 app = XtCreateApplicationContext ();
2278 XtAppSetFallbackResources (app, defaults);
2279 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
2280 toplevel_shell = XtAppCreateShell (progname, progclass,
2281 applicationShellWidgetClass,
2284 dpy = XtDisplay (toplevel_shell);
2285 db = XtDatabase (dpy);
2286 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
2287 XSetErrorHandler (demo_ehandler);
2290 /* After doing Xt-style command-line processing, complain about any
2291 unrecognized command-line arguments.
2293 for (i = 1; i < argc; i++)
2296 if (s[0] == '-' && s[1] == '-')
2298 if (!strcmp (s, "-prefs"))
2300 else if (crapplet_p)
2301 /* There are lots of random args that we don't care about when we're
2302 started as a crapplet, so just ignore unknown args in that case. */
2306 fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]);
2312 /* Load the init file, which may end up consulting the X resource database
2313 and the site-wide app-defaults file. Note that at this point, it's
2314 important that `progname' be "xscreensaver", rather than whatever
2321 /* Now that Xt has been initialized, and the resources have been read,
2322 we can set our `progname' variable to something more in line with
2325 progname = real_progname;
2329 /* Print out all the resources we read. */
2331 XrmName name = { 0 };
2332 XrmClass class = { 0 };
2334 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
2340 /* Intern the atoms that xscreensaver_command() needs.
2342 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
2343 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
2344 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
2345 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
2346 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
2347 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
2348 XA_SELECT = XInternAtom (dpy, "SELECT", False);
2349 XA_DEMO = XInternAtom (dpy, "DEMO", False);
2350 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
2351 XA_BLANK = XInternAtom (dpy, "BLANK", False);
2352 XA_LOCK = XInternAtom (dpy, "LOCK", False);
2353 XA_EXIT = XInternAtom (dpy, "EXIT", False);
2354 XA_RESTART = XInternAtom (dpy, "RESTART", False);
2357 /* Create the window and all its widgets.
2359 gtk_window = create_xscreensaver_demo ();
2360 toplevel_widget = gtk_window;
2362 /* Set the window's title. */
2365 char *v = (char *) strdup(strchr(screensaver_id, ' '));
2366 char *s1, *s2, *s3, *s4;
2367 s1 = (char *) strchr(v, ' '); s1++;
2368 s2 = (char *) strchr(s1, ' ');
2369 s3 = (char *) strchr(v, '('); s3++;
2370 s4 = (char *) strchr(s3, ')');
2373 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
2374 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
2378 /* Various other widget initializations...
2380 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
2381 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
2383 populate_hack_list (gtk_window, pair);
2384 populate_prefs_page (gtk_window, pair);
2385 sensitize_demo_widgets (gtk_window, False);
2386 fix_text_entry_sizes (gtk_window);
2387 scroll_to_current_hack (gtk_window, pair);
2389 gtk_signal_connect (
2390 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")),
2391 "map", GTK_SIGNAL_FUNC(map_window_cb), 0);
2392 gtk_signal_connect (
2393 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")),
2394 "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0);
2395 gtk_signal_connect (
2396 GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")),
2397 "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0);
2400 /* Handle the -prefs command-line argument. */
2403 GtkNotebook *notebook =
2404 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
2405 gtk_notebook_set_page (notebook, 1);
2408 # ifdef HAVE_CRAPPLET
2412 GtkWidget *top_vbox;
2414 capplet = capplet_widget_new ();
2416 top_vbox = GTK_BIN (gtk_window)->child;
2418 gtk_widget_ref (top_vbox);
2419 gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
2420 GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
2422 /* In crapplet-mode, take off the menubar. */
2423 gtk_widget_hide (name_to_widget (gtk_window, "menubar"));
2425 gtk_container_add (GTK_CONTAINER (capplet), top_vbox);
2426 gtk_widget_show (capplet);
2427 gtk_widget_hide (gtk_window);
2429 /* Hook up the Control Center's redundant Help button, too. */
2430 gtk_signal_connect (GTK_OBJECT (capplet), "help",
2431 GTK_SIGNAL_FUNC (doc_menu_cb), 0);
2433 /* Issue any warnings about the running xscreensaver daemon. */
2434 the_network_is_not_the_computer (top_vbox);
2437 # endif /* HAVE_CRAPPLET */
2439 gtk_widget_show (gtk_window);
2440 init_icon (GTK_WIDGET(gtk_window)->window);
2442 /* Issue any warnings about the running xscreensaver daemon. */
2443 the_network_is_not_the_computer (gtk_window);
2446 /* Run the Gtk event loop, and not the Xt event loop. This means that
2447 if there were Xt timers or fds registered, they would never get serviced,
2448 and if there were any Xt widgets, they would never have events delivered.
2449 Fortunately, we're using Gtk for all of the UI, and only initialized
2450 Xt so that we could process the command line and use the X resource
2453 initializing_p = False;
2455 # ifdef HAVE_CRAPPLET
2457 capplet_gtk_main ();
2459 # endif /* HAVE_CRAPPLET */
2465 #endif /* HAVE_GTK -- whole file */