1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-1998 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 extern Display *gdk_display;
65 #include "resources.h" /* for parse_time() */
66 #include "visual.h" /* for has_writable_cells() */
67 #include "remote.h" /* for xscreensaver_command() */
70 #include "demo-Gtk-widgets.h"
77 #define countof(x) (sizeof((x))/sizeof((*x)))
81 char *progclass = "XScreenSaver";
85 saver_preferences *a, *b;
88 static void *global_prefs_pair; /* I hate C so much... */
92 char *blurb (void) { return progname; }
94 static void run_hack (int which);
96 static char *short_version = 0;
99 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
100 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
101 Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
105 static void populate_demo_window (GtkWidget *toplevel,
106 int which, prefs_pair *pair);
107 static void populate_prefs_page (GtkWidget *top, prefs_pair *pair);
111 /* Some random utility functions
115 name_to_widget (GtkWidget *widget, const char *name)
119 GtkWidget *parent = (GTK_IS_MENU (widget)
120 ? gtk_menu_get_attach_widget (GTK_MENU (widget))
127 return (GtkWidget *) gtk_object_get_data (GTK_OBJECT (widget), name);
132 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
133 Takes a scroller, viewport, or list as an argument.
136 ensure_selected_item_visible (GtkWidget *widget)
138 GtkScrolledWindow *scroller = 0;
140 GtkList *list_widget = 0;
144 GtkWidget *selected = 0;
147 gint parent_h, child_y, child_h, children_h, ignore;
148 double ratio_t, ratio_b;
150 if (GTK_IS_SCROLLED_WINDOW (widget))
152 scroller = GTK_SCROLLED_WINDOW (widget);
153 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
154 list_widget = GTK_LIST (GTK_BIN(vp)->child);
156 else if (GTK_IS_VIEWPORT (widget))
158 vp = GTK_VIEWPORT (widget);
159 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
160 list_widget = GTK_LIST (GTK_BIN(vp)->child);
162 else if (GTK_IS_LIST (widget))
164 list_widget = GTK_LIST (widget);
165 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
166 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
171 slist = list_widget->selection;
172 selected = (slist ? GTK_WIDGET (slist->data) : 0);
176 which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
178 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
179 kids; kids = kids->next)
182 adj = gtk_scrolled_window_get_vadjustment (scroller);
184 gdk_window_get_geometry (GTK_WIDGET(vp)->window,
185 &ignore, &ignore, &ignore, &parent_h, &ignore);
186 gdk_window_get_geometry (GTK_WIDGET(selected)->window,
187 &ignore, &child_y, &ignore, &child_h, &ignore);
188 children_h = nkids * child_h;
190 ratio_t = ((double) child_y) / ((double) children_h);
191 ratio_b = ((double) child_y + child_h) / ((double) children_h);
193 if (ratio_t < (adj->value / adj->upper) ||
194 ratio_b > ((adj->value + adj->page_size) / adj->upper))
197 int slop = parent_h * 0.75; /* how much to overshoot by */
199 if (ratio_t < (adj->value / adj->upper))
201 double ratio_w = ((double) parent_h) / ((double) children_h);
202 double ratio_l = (ratio_b - ratio_t);
203 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
206 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
208 target = ratio_t * adj->upper;
212 if (target > adj->upper - adj->page_size)
213 target = adj->upper - adj->page_size;
217 gtk_adjustment_set_value (adj, target);
223 warning_dialog_dismiss_cb (GtkButton *button, gpointer user_data)
225 GtkWidget *shell = GTK_WIDGET (user_data);
226 while (shell->parent)
227 shell = shell->parent;
228 gtk_widget_destroy (GTK_WIDGET (shell));
233 warning_dialog (GtkWidget *parent, const char *message, int center)
235 char *msg = strdup (message);
238 GtkWidget *dialog = gtk_dialog_new ();
239 GtkWidget *label = 0;
243 while (parent->parent)
244 parent = parent->parent;
250 char *s = strchr (head, '\n');
253 sprintf (name, "label%d", i++);
257 label = gtk_label_new (head);
258 sprintf (buf, "warning_dialog.%s.font", name);
259 GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style);
260 GTK_WIDGET (label)->style->font =
261 gdk_font_load (get_string_resource (buf, "Dialog.Label.Font"));
263 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
264 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
265 label, TRUE, TRUE, 0);
266 gtk_widget_show (label);
277 label = gtk_label_new ("");
278 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
279 label, TRUE, TRUE, 0);
280 gtk_widget_show (label);
282 label = gtk_hbutton_box_new ();
283 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
284 label, TRUE, TRUE, 0);
286 ok = gtk_button_new_with_label (
287 get_string_resource ("warning_dialog.ok.label",
288 "warning_dialog.Button.Label"));
289 gtk_container_add (GTK_CONTAINER (label), ok);
291 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
292 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
293 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
294 gtk_widget_show (ok);
295 gtk_widget_show (label);
296 gtk_widget_show (dialog);
297 /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
299 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
300 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
302 gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
303 GTK_WIDGET (parent)->window);
305 gdk_window_show (GTK_WIDGET (dialog)->window);
306 gdk_window_raise (GTK_WIDGET (dialog)->window);
316 if (which < 0) return;
317 status = xscreensaver_command (gdk_display, XA_DEMO, which + 1, False);
323 "The DEMO %d command failed (%d).\n", which + 1, status);
325 warning_dialog (GTK_WIDGET (menuitem), buf, 1);
336 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
342 wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
344 exit_menu_cb (NULL, NULL);
349 cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
352 warning_dialog (GTK_WIDGET (menuitem),
354 "cut unimplemented\n", 1);
359 copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
362 warning_dialog (GTK_WIDGET (menuitem),
364 "copy unimplemented\n", 1);
369 paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
372 warning_dialog (GTK_WIDGET (menuitem),
374 "paste unimplemented\n", 1);
379 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
382 char *s = strdup (screensaver_id + 4);
385 s2 = strchr (s, ',');
389 sprintf (buf, "%s\n%s\n\n"
390 "For updates, check http://www.jwz.org/xscreensaver/",
394 warning_dialog (GTK_WIDGET (menuitem), buf, 100);
399 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
401 /* prefs_pair *pair = (prefs_pair *) client_data; */
402 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
404 saver_preferences *p = pair->a;
407 if (!p->help_url || !*p->help_url)
409 warning_dialog (GTK_WIDGET (menuitem),
411 "No Help URL has been specified.\n", 100);
415 help_command = (char *) malloc (strlen (p->load_url_command) +
416 (strlen (p->help_url) * 2) + 20);
417 strcpy (help_command, "( ");
418 sprintf (help_command + strlen(help_command),
419 p->load_url_command, p->help_url, p->help_url);
420 strcat (help_command, " ) &");
421 system (help_command);
427 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
429 int status = xscreensaver_command (gdk_display, XA_ACTIVATE, 0, False);
435 "The ACTIVATE command failed (%d).\n", status);
436 warning_dialog (GTK_WIDGET (menuitem), buf, 100);
442 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
444 int status = xscreensaver_command (gdk_display, XA_LOCK, 0, False);
450 "The LOCK command failed (%d).\n", status);
451 warning_dialog (GTK_WIDGET (menuitem), buf, 100);
457 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
459 int status = xscreensaver_command (gdk_display, XA_EXIT, 0, False);
465 "The EXIT command failed (%d).\n", status);
466 warning_dialog (GTK_WIDGET (menuitem), buf, 100);
472 restart_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
475 int status = xscreensaver_command (gdk_display, XA_RESTART, 0, False);
481 "The RESTART command failed (%d).\n", status);
482 warning_dialog (GTK_WIDGET (menuitem), buf, 100);
485 xscreensaver_command (gdk_display, XA_EXIT, 0, False);
487 system ("xscreensaver -nosplash &");
493 selected_hack_number (GtkWidget *toplevel)
495 GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport"));
496 GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child);
497 GList *slist = list_widget->selection;
498 GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
499 int which = (selected
500 ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
507 apply_this_cb (GtkButton *button, gpointer user_data)
509 /* prefs_pair *pair = (prefs_pair *) client_data; */
510 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
512 saver_preferences *p = pair->a;
513 GtkList *list_widget =
514 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
515 int which = selected_hack_number (GTK_WIDGET (button));
517 GtkEntry *cmd = GTK_ENTRY (name_to_widget (GTK_WIDGET (button), "cmd_text"));
518 GtkToggleButton *enabled =
519 GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET (button), "enabled"));
520 GtkCombo *vis = GTK_COMBO (name_to_widget (GTK_WIDGET (button),
523 Bool enabled_p = gtk_toggle_button_get_active (enabled);
524 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
525 const char *command = gtk_entry_get_text (cmd);
530 if (which < 0) return;
532 /* Sanity-check and canonicalize whatever the user typed into the combo box.
534 if (!strcasecmp (visual, "any")) visual = "";
535 if (!strcasecmp (visual, "default")) visual = "Default";
536 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
537 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
538 else if (!strcasecmp (visual, "best")) visual = "Best";
539 else if (!strcasecmp (visual, "mono")) visual = "Mono";
540 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
541 else if (!strcasecmp (visual, "gray")) visual = "Gray";
542 else if (!strcasecmp (visual, "grey")) visual = "Gray";
543 else if (!strcasecmp (visual, "color")) visual = "Color";
544 else if (!strcasecmp (visual, "gl")) visual = "GL";
545 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
546 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
547 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
548 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
549 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
550 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
551 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
552 else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
553 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
556 gdk_beep (); /* unparsable */
558 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
561 p->screenhacks[which]->enabled_p = enabled_p;
562 if (p->screenhacks[which]->visual)
563 free (p->screenhacks[which]->visual);
564 if (p->screenhacks[which]->command)
565 free (p->screenhacks[which]->command);
566 p->screenhacks[which]->visual = strdup (visual);
567 p->screenhacks[which]->command = strdup (command);
569 ensure_selected_item_visible (GTK_WIDGET (list_widget));
571 write_init_file (p, short_version);
576 run_this_cb (GtkButton *button, gpointer user_data)
578 int which = selected_hack_number (GTK_WIDGET (button));
579 if (which < 0) return;
580 apply_this_cb (button, user_data);
586 cancel_this_cb (GtkButton *button, gpointer user_data)
588 /* prefs_pair *pair = (prefs_pair *) client_data; */
589 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
590 GtkList *list_widget =
591 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
592 int which = selected_hack_number (GTK_WIDGET (button));
593 if (which < 0) return;
594 ensure_selected_item_visible (GTK_WIDGET (list_widget));
595 populate_demo_window (GTK_WIDGET (button), which, pair);
600 run_next_cb (GtkButton *button, gpointer user_data)
602 /* prefs_pair *pair = (prefs_pair *) client_data; */
603 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
604 saver_preferences *p = pair->a;
606 GtkList *list_widget =
607 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
608 int which = selected_hack_number (GTK_WIDGET (button));
615 if (which >= p->screenhacks_count)
618 gtk_list_select_item (GTK_LIST (list_widget), which);
619 ensure_selected_item_visible (GTK_WIDGET (list_widget));
620 populate_demo_window (GTK_WIDGET (button), which, pair);
626 run_prev_cb (GtkButton *button, gpointer user_data)
628 /* prefs_pair *pair = (prefs_pair *) client_data; */
629 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
630 saver_preferences *p = pair->a;
632 GtkList *list_widget =
633 GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
634 int which = selected_hack_number (GTK_WIDGET (button));
637 which = p->screenhacks_count - 1;
642 which = p->screenhacks_count - 1;
644 gtk_list_select_item (GTK_LIST (list_widget), which);
645 ensure_selected_item_visible (GTK_WIDGET (list_widget));
646 populate_demo_window (GTK_WIDGET (button), which, pair);
651 /* Helper for the text fields that contain time specifications:
652 this parses the text, and does error checking.
655 hack_time_text (const char *line, Time *store, Bool sec_p)
660 value = parse_time ((char *) line, sec_p, True);
661 value *= 1000; /* Time measures in microseconds */
671 prefs_ok_cb (GtkButton *button, gpointer user_data)
673 /* prefs_pair *pair = (prefs_pair *) client_data; */
674 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
676 saver_preferences *p = pair->a;
677 saver_preferences *p2 = pair->b;
679 # define SECONDS(field, name) \
680 hack_time_text (gtk_entry_get_text (\
681 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
685 # define MINUTES(field, name) \
686 hack_time_text (gtk_entry_get_text (\
687 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
691 # define INTEGER(field, name) do { \
692 char *line = gtk_entry_get_text (\
693 GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
694 unsigned int value; \
698 else if (sscanf (line, "%u%c", &value, &c) != 1) \
704 MINUTES (&p2->timeout, "timeout_text");
705 MINUTES (&p2->cycle, "cycle_text");
706 SECONDS (&p2->fade_seconds, "fade_text");
707 INTEGER (&p2->fade_ticks, "ticks_text");
708 MINUTES (&p2->lock_timeout, "lock_text");
709 SECONDS (&p2->passwd_timeout, "pass_text");
715 p->timeout = p2->timeout;
716 p->cycle = p2->cycle;
717 p->lock_timeout = p2->lock_timeout;
718 p->passwd_timeout = p2->passwd_timeout;
719 p->fade_seconds = p2->fade_seconds;
720 p->fade_ticks = p2->fade_ticks;
721 p->verbose_p = p2->verbose_p;
722 p->install_cmap_p = p2->install_cmap_p;
723 p->fade_p = p2->fade_p;
724 p->unfade_p = p2->unfade_p;
725 p->lock_p = p2->lock_p;
727 populate_prefs_page (GTK_WIDGET (button), pair);
729 write_init_file (p, short_version);
734 prefs_cancel_cb (GtkButton *button, gpointer user_data)
736 /* prefs_pair *pair = (prefs_pair *) client_data; */
737 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
740 populate_prefs_page (GTK_WIDGET (button), pair);
745 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
746 gpointer client_data)
748 if (event->type == GDK_2BUTTON_PRESS)
750 GtkList *list = GTK_LIST (name_to_widget (button, "list"));
751 int which = gtk_list_child_position (list, GTK_WIDGET (button));
762 list_select_cb (GtkList *list, GtkWidget *child)
764 /* prefs_pair *pair = (prefs_pair *) client_data; */
765 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
767 int which = gtk_list_child_position (list, GTK_WIDGET (child));
768 populate_demo_window (GTK_WIDGET (list), which, pair);
772 list_unselect_cb (GtkList *list, GtkWidget *child)
774 /* prefs_pair *pair = (prefs_pair *) client_data; */
775 prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
777 populate_demo_window (GTK_WIDGET (list), -1, pair);
781 /* Populating the various widgets
785 /* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
788 format_time (char *buf, Time time)
791 unsigned int h = 0, m = 0;
802 sprintf (buf, "%u:%02u:%02u", h, m, s);
807 make_pretty_name (const char *shell_command)
809 char *s = strdup (shell_command);
813 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
820 s2 = strrchr (s, '/'); /* if pathname, take last component */
828 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
831 sprintf (res_name, "hacks.%s.name", s); /* resource? */
832 s2 = get_string_resource (res_name, res_name);
836 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
837 if (*s2 >= 'A' && *s2 <= 'Z')
840 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
842 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
848 /* Finds the number of the last hack to run, and makes that item be
852 scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
856 unsigned long nitems, bytesafter;
858 Display *dpy = gdk_display;
862 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
863 XA_SCREENSAVER_STATUS,
864 0, 3, False, XA_INTEGER,
865 &type, &format, &nitems, &bytesafter,
866 (unsigned char **) &data)
868 && type == XA_INTEGER
871 which = (int) data[2] - 1;
873 if (data) free (data);
878 list = GTK_LIST (name_to_widget (toplevel, "list"));
879 gtk_list_select_item (list, which);
880 ensure_selected_item_visible (GTK_WIDGET (list));
881 populate_demo_window (toplevel, which, pair);
887 populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
889 saver_preferences *p = pair->a;
890 GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
891 screenhack **hacks = p->screenhacks;
894 for (h = hacks; *h; h++)
897 char *pretty_name = (h[0]->name
898 ? strdup (h[0]->name)
899 : make_pretty_name (h[0]->command));
901 line = gtk_list_item_new_with_label (pretty_name);
904 gtk_container_add (GTK_CONTAINER (list), line);
905 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
906 GTK_SIGNAL_FUNC (list_doubleclick_cb),
909 GTK_WIDGET (GTK_BIN(line)->child)->style =
910 gtk_style_copy (GTK_WIDGET (text_line)->style);
912 gtk_widget_show (line);
915 gtk_signal_connect (GTK_OBJECT (list), "select_child",
916 GTK_SIGNAL_FUNC (list_select_cb),
918 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
919 GTK_SIGNAL_FUNC (list_unselect_cb),
925 populate_prefs_page (GtkWidget *top, prefs_pair *pair)
927 saver_preferences *p = pair->a;
930 format_time (s, p->timeout);
931 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
932 format_time (s, p->cycle);
933 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
934 format_time (s, p->lock_timeout);
935 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
936 format_time (s, p->passwd_timeout);
937 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "pass_text")), s);
938 format_time (s, p->fade_seconds);
939 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
940 sprintf (s, "%u", p->fade_ticks);
941 gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "ticks_text")), s);
943 gtk_toggle_button_set_active (
944 GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
946 gtk_toggle_button_set_active (
947 GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
949 gtk_toggle_button_set_active (
950 GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
952 gtk_toggle_button_set_active (
953 GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
955 gtk_toggle_button_set_active (
956 GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
961 Bool found_any_writable_cells = False;
962 Display *dpy = gdk_display;
963 int nscreens = ScreenCount(dpy);
965 for (i = 0; i < nscreens; i++)
967 Screen *s = ScreenOfDisplay (dpy, i);
968 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
970 found_any_writable_cells = True;
975 gtk_widget_set_sensitive (
976 GTK_WIDGET (name_to_widget (top, "fade_label")),
977 found_any_writable_cells);
978 gtk_widget_set_sensitive (
979 GTK_WIDGET (name_to_widget (top, "ticks_label")),
980 found_any_writable_cells);
981 gtk_widget_set_sensitive (
982 GTK_WIDGET (name_to_widget (top, "fade_text")),
983 found_any_writable_cells);
984 gtk_widget_set_sensitive (
985 GTK_WIDGET (name_to_widget (top, "ticks_text")),
986 found_any_writable_cells);
987 gtk_widget_set_sensitive (
988 GTK_WIDGET (name_to_widget (top, "install_button")),
989 found_any_writable_cells);
990 gtk_widget_set_sensitive (
991 GTK_WIDGET (name_to_widget (top, "fade_button")),
992 found_any_writable_cells);
993 gtk_widget_set_sensitive (
994 GTK_WIDGET (name_to_widget (top, "unfade_button")),
995 found_any_writable_cells);
1002 sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
1004 const char *names[] = { "cmd_label", "cmd_text", "enabled",
1005 "visual", "visual_combo",
1006 "demo", "apply", "cancel" };
1008 for (i = 0; i < countof(names); i++)
1010 GtkWidget *w = name_to_widget (toplevel, names[i]);
1011 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
1014 /* I don't know how to handle these yet... */
1016 const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
1017 for (i = 0; i < countof(names2); i++)
1019 GtkWidget *w = name_to_widget (toplevel, names2[i]);
1020 gtk_widget_set_sensitive (GTK_WIDGET(w), False);
1026 /* Even though we've given these text fields a maximum number of characters,
1027 their default size is still about 30 characters wide -- so measure out
1028 a string in their font, and resize them to just fit that.
1031 fix_text_entry_sizes (GtkWidget *toplevel)
1033 const char *names[] = { "timeout_text", "cycle_text", "fade_text",
1034 "ticks_text", "lock_text", "pass_text" };
1039 for (i = 0; i < countof(names); i++)
1041 w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
1043 width = gdk_text_width (w->style->font, "00:00:00_", 9);
1044 gtk_widget_set_usize (w, width, -2);
1047 /* Now fix the size of the combo box.
1049 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
1050 w = GTK_COMBO (w)->entry;
1051 width = gdk_text_width (w->style->font, "PseudoColor___", 14);
1052 gtk_widget_set_usize (w, width, -2);
1055 /* Now fix the size of the list.
1057 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
1058 width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
1059 gtk_widget_set_usize (w, width, -2);
1066 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
1069 static char *up_arrow_xpm[] = {
1092 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1093 the end of the array (Gtk 1.2.5.) */
1094 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1095 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1098 static char *down_arrow_xpm[] = {
1120 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
1121 the end of the array (Gtk 1.2.5.) */
1122 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
1123 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
1127 pixmapify_buttons (GtkWidget *toplevel)
1131 GtkWidget *pixmapwid;
1135 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "next"));
1136 style = gtk_widget_get_style (w);
1138 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1139 &style->bg[GTK_STATE_NORMAL],
1140 (gchar **) down_arrow_xpm);
1141 pixmapwid = gtk_pixmap_new (pixmap, mask);
1142 gtk_widget_show (pixmapwid);
1143 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1144 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1146 w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "prev"));
1147 style = gtk_widget_get_style (w);
1149 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
1150 &style->bg[GTK_STATE_NORMAL],
1151 (gchar **) up_arrow_xpm);
1152 pixmapwid = gtk_pixmap_new (pixmap, mask);
1153 gtk_widget_show (pixmapwid);
1154 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
1155 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
1160 /* Work around a Gtk bug that causes label widgets to wrap text too early.
1164 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
1165 GtkAllocation *allocation,
1169 GtkWidgetAuxInfo *aux_info;
1171 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
1173 aux_info->width = allocation->width;
1174 aux_info->height = -2;
1178 gtk_widget_size_request (label, &req);
1182 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
1185 eschew_gtk_lossage (GtkWidget *toplevel)
1187 GtkWidgetAuxInfo *aux_info;
1188 GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
1190 aux_info = g_new0 (GtkWidgetAuxInfo, 1);
1191 aux_info->width = label->allocation.width;
1192 aux_info->height = -2;
1196 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
1198 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
1199 you_are_not_a_unique_or_beautiful_snowflake, NULL);
1201 gtk_widget_queue_resize (label);
1206 get_hack_blurb (screenhack *hack)
1209 char *prog_name = strdup (hack->command);
1210 char *pretty_name = (hack->name
1211 ? strdup (hack->name)
1212 : make_pretty_name (hack->command));
1213 char doc_name[255], doc_class[255];
1216 for (s = prog_name; *s && !isspace(*s); s++)
1219 s = strrchr (prog_name, '/');
1220 if (s) strcpy (prog_name, s+1);
1222 sprintf (doc_name, "hacks.%s.documentation", pretty_name);
1223 sprintf (doc_class, "hacks.%s.documentation", prog_name);
1227 doc_string = get_string_resource (doc_name, doc_class);
1230 for (s = doc_string; *s; s++)
1234 /* skip over whitespace at beginning of line */
1236 while (*s && (*s == ' ' || *s == '\t'))
1239 else if (*s == ' ' || *s == '\t')
1241 /* compress all other horizontal whitespace. */
1244 for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
1246 if (s2 > s) strcpy (s, s2);
1251 while (*s && isspace (*s)) /* Strip trailing whitespace */
1254 /* Delete whitespace at end of each line. */
1255 for (; s > doc_string; s--)
1256 if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
1259 s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
1263 if (s2 < s) strcpy (s2, s);
1267 /* Delete leading blank lines. */
1268 for (s = doc_string; *s == '\n'; s++)
1270 if (s > doc_string) strcpy (doc_string, s);
1274 static int doc_installed = 0;
1275 if (doc_installed == 0)
1277 if (get_boolean_resource ("hacks.documentation.isInstalled",
1278 "hacks.documentation.isInstalled"))
1284 if (doc_installed < 0)
1286 strdup ("Error:\n\n"
1287 "The documentation strings do not appear to be "
1288 "installed. This is probably because there is "
1289 "an \"XScreenSaver\" app-defaults file installed "
1290 "that is from an older version of the program. "
1291 "To fix this problem, delete that file, or "
1292 "install a current version (either will work.)");
1294 doc_string = strdup ("");
1302 populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
1304 saver_preferences *p = pair->a;
1305 screenhack *hack = (which >= 0 ? p->screenhacks[which] : 0);
1306 GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame"));
1307 GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc"));
1308 GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text"));
1309 GtkToggleButton *enabled =
1310 GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
1311 GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
1313 char *pretty_name = (hack
1315 ? strdup (hack->name)
1316 : make_pretty_name (hack->command))
1318 char *doc_string = hack ? get_hack_blurb (hack) : 0;
1320 gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
1321 gtk_label_set_text (doc, (doc_string ? doc_string : ""));
1322 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
1323 gtk_entry_set_position (cmd, 0);
1324 gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False));
1325 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
1327 ? (hack->visual && hack->visual
1332 gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
1334 sensitize_demo_widgets (toplevel, (hack ? True : False));
1336 if (pretty_name) free (pretty_name);
1337 if (doc_string) free (doc_string);
1343 /* The main demo-mode command loop.
1348 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
1349 XrmRepresentation *type, XrmValue *value, XPointer closure)
1352 for (i = 0; quarks[i]; i++)
1354 if (bindings[i] == XrmBindTightly)
1355 fprintf (stderr, (i == 0 ? "" : "."));
1356 else if (bindings[i] == XrmBindLoosely)
1357 fprintf (stderr, "*");
1359 fprintf (stderr, " ??? ");
1360 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
1363 fprintf (stderr, ": %s\n", (char *) value->addr);
1371 the_network_is_not_the_computer (GtkWidget *parent)
1373 Display *dpy = gdk_display;
1374 char *rversion, *ruser, *rhost;
1375 char *luser, *lhost;
1377 struct passwd *p = getpwuid (getuid ());
1378 const char *d = DisplayString (dpy);
1380 # if defined(HAVE_UNAME)
1382 if (uname (&uts) < 0)
1383 lhost = "<UNKNOWN>";
1385 lhost = uts.nodename;
1387 strcpy (lhost, getenv("SYS$NODE"));
1388 # else /* !HAVE_UNAME && !VMS */
1389 strcat (lhost, "<UNKNOWN>");
1390 # endif /* !HAVE_UNAME && !VMS */
1392 if (p && p->pw_name)
1397 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
1399 /* Make a buffer that's big enough for a number of copies of all the
1400 strings, plus some. */
1401 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
1402 (ruser ? strlen(ruser) : 0) +
1403 (rhost ? strlen(rhost) : 0) +
1410 if (!rversion || !*rversion)
1414 "The XScreenSaver daemon doesn't seem to be running\n"
1415 "on display \"%s\". You can launch it by selecting\n"
1416 "`Restart Daemon' from the File menu, or by typing\n"
1417 "\"xscreensaver &\" in a shell.",
1420 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
1422 /* Warn that the two processes are running as different users.
1426 "%s is running as user \"%s\" on host \"%s\".\n"
1427 "But the xscreensaver managing display \"%s\"\n"
1428 "is running as user \"%s\" on host \"%s\".\n"
1430 "Since they are different users, they won't be reading/writing\n"
1431 "the same ~/.xscreensaver file, so %s isn't\n"
1432 "going to work right.\n"
1434 "Either re-run %s as \"%s\", or re-run\n"
1435 "xscreensaver as \"%s\" (which you can do by\n"
1436 "selecting `Restart Daemon' from the File menu.)\n",
1437 progname, luser, lhost,
1439 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1441 progname, (ruser ? ruser : "???"),
1444 else if (rhost && *rhost && !!strcmp (rhost, lhost))
1446 /* Warn that the two processes are running on different hosts.
1450 "%s is running as user \"%s\" on host \"%s\".\n"
1451 "But the xscreensaver managing display \"%s\"\n"
1452 "is running as user \"%s\" on host \"%s\".\n"
1454 "If those two machines don't share a file system (that is,\n"
1455 "if they don't see the same ~%s/.xscreensaver file) then\n"
1456 "%s won't work right.\n"
1458 "You can restart the daemon on \"%s\" as \"%s\" by\n"
1459 "selecting `Restart Daemon' from the File menu.)",
1460 progname, luser, lhost,
1462 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
1467 else if (!!strcmp (rversion, short_version))
1469 /* Warn that the version numbers don't match.
1473 "This is %s version %s.\n"
1474 "But the xscreensaver managing display \"%s\"\n"
1475 "is version %s. This could cause problems.",
1476 progname, short_version,
1483 warning_dialog (parent, msg, 1);
1489 /* We use this error handler so that X errors are preceeded by the name
1490 of the program that generated them.
1493 demo_ehandler (Display *dpy, XErrorEvent *error)
1495 fprintf (stderr, "\nX error in %s:\n", progname);
1496 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
1499 fprintf (stderr, " (nonfatal.)\n");
1504 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
1505 of the program that generated them; and also that we can ignore one
1506 particular bogus error message that Gdk madly spews.
1509 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
1510 const gchar *message, gpointer user_data)
1512 /* Ignore the message "Got event for unknown window: 0x...".
1513 Apparently some events are coming in for the xscreensaver window
1514 (presumably reply events related to the ClientMessage) and Gdk
1515 feels the need to complain about them. So, just suppress any
1516 messages that look like that one.
1518 if (strstr (message, "unknown window"))
1521 fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
1522 (log_level == G_LOG_LEVEL_ERROR ? "error" :
1523 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
1524 log_level == G_LOG_LEVEL_WARNING ? "warning" :
1525 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
1526 log_level == G_LOG_LEVEL_INFO ? "info" :
1527 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
1529 ((!*message || message[strlen(message)-1] != '\n')
1534 static char *defaults[] = {
1535 #include "XScreenSaver_ad.h"
1540 main (int argc, char **argv)
1543 prefs_pair Pair, *pair;
1544 saver_preferences P, P2, *p, *p2;
1548 Widget toplevel_shell;
1549 GtkWidget *gtk_window;
1550 char *real_progname = argv[0];
1553 s = strrchr (real_progname, '/');
1554 if (s) real_progname = s+1;
1561 memset (p, 0, sizeof (*p));
1562 memset (p2, 0, sizeof (*p2));
1564 global_prefs_pair = pair; /* I hate C so much... */
1566 progname = real_progname;
1568 short_version = (char *) malloc (5);
1569 memcpy (short_version, screensaver_id + 17, 4);
1570 short_version [4] = 0;
1573 /* Register our error message logger for every ``log domain'' known.
1574 There's no way to do this globally, so I grepped the Gtk/Gdk sources
1575 for all of the domains that seem to be in use.
1578 const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
1579 "GThread", "Gnome", "GnomeUI", 0 };
1580 for (i = 0; domains[i]; i++)
1581 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
1584 /* This is gross, but Gtk understands --display and not -display...
1586 for (i = 1; i < argc; i++)
1587 if (argv[i][0] && argv[i][1] &&
1588 !strncmp(argv[i], "-display", strlen(argv[i])))
1589 argv[i] = "--display";
1591 /* Let Gtk open the X connection, then initialize Xt to use that
1592 same connection. Doctor Frankenstein would be proud. */
1593 gtk_init (&argc, &argv);
1596 /* We must read exactly the same resources as xscreensaver.
1597 That means we must have both the same progclass *and* progname,
1598 at least as far as the resource database is concerned. So,
1599 put "xscreensaver" in argv[0] while initializing Xt.
1601 argv[0] = "xscreensaver";
1605 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
1607 XtToolkitInitialize ();
1608 app = XtCreateApplicationContext ();
1610 XtAppSetFallbackResources (app, defaults);
1611 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
1612 toplevel_shell = XtAppCreateShell (progname, progclass,
1613 applicationShellWidgetClass,
1616 dpy = XtDisplay (toplevel_shell);
1617 db = XtDatabase (dpy);
1618 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
1619 XSetErrorHandler (demo_ehandler);
1622 /* After doing Xt-style command-line processing, complain about any
1623 unrecognized command-line arguments.
1625 for (i = 1; i < argc; i++)
1628 if (s[0] == '-' && s[1] == '-')
1630 if (!strcmp (s, "-prefs"))
1634 fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n",
1640 /* Load the init file, which may end up consulting the X resource database
1641 and the site-wide app-defaults file. Note that at this point, it's
1642 important that `progname' be "xscreensaver", rather than whatever
1649 /* Now that Xt has been initialized, and the resources have been read,
1650 we can set our `progname' variable to something more in line with
1653 progname = real_progname;
1657 /* Print out all the resources we read. */
1659 XrmName name = { 0 };
1660 XrmClass class = { 0 };
1662 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
1668 /* Intern the atoms that xscreensaver_command() needs.
1670 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1671 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
1672 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
1673 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
1674 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
1675 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
1676 XA_SELECT = XInternAtom (dpy, "SELECT", False);
1677 XA_DEMO = XInternAtom (dpy, "DEMO", False);
1678 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
1679 XA_BLANK = XInternAtom (dpy, "BLANK", False);
1680 XA_LOCK = XInternAtom (dpy, "LOCK", False);
1681 XA_EXIT = XInternAtom (dpy, "EXIT", False);
1682 XA_RESTART = XInternAtom (dpy, "RESTART", False);
1685 /* Create the window and all its widgets.
1687 gtk_window = create_xscreensaver_demo ();
1689 /* Set the window's title. */
1692 char *v = (char *) strdup(strchr(screensaver_id, ' '));
1693 char *s1, *s2, *s3, *s4;
1694 s1 = (char *) strchr(v, ' '); s1++;
1695 s2 = (char *) strchr(s1, ' ');
1696 s3 = (char *) strchr(v, '('); s3++;
1697 s4 = (char *) strchr(s3, ')');
1700 sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
1701 gtk_window_set_title (GTK_WINDOW (gtk_window), title);
1705 /* Various other widget initializations...
1707 gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
1708 GTK_SIGNAL_FUNC (wm_close_cb), NULL);
1710 populate_hack_list (gtk_window, pair);
1711 populate_prefs_page (gtk_window, pair);
1712 sensitize_demo_widgets (gtk_window, False);
1713 fix_text_entry_sizes (gtk_window);
1714 scroll_to_current_hack (gtk_window, pair);
1715 gtk_widget_show (gtk_window);
1717 /* The next three calls must come after gtk_widget_show(). */
1718 pixmapify_buttons (gtk_window);
1719 eschew_gtk_lossage (gtk_window);
1720 ensure_selected_item_visible (GTK_WIDGET(name_to_widget(gtk_window,"list")));
1722 /* Handle the -prefs command-line argument. */
1725 GtkNotebook *notebook =
1726 GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
1727 gtk_notebook_set_page (notebook, 1);
1730 /* Issue any warnings about the running xscreensaver daemon. */
1731 the_network_is_not_the_computer (gtk_window);
1733 /* Run the Gtk event loop, and not the Xt event loop. This means that
1734 if there were Xt timers or fds registered, they would never get serviced,
1735 and if there were any Xt widgets, they would never have events delivered.
1736 Fortunately, we're using Gtk for all of the UI, and only initialized
1737 Xt so that we could process the command line and use the X resource
1744 #endif /* HAVE_GTK -- whole file */