X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fdemo-Gtk.c;h=0ff66e9382ae0a7bcbb721edff8ad66b1cb439c4;hb=de041722414a2e31c1c04caa10aaec9d6952e9b4;hp=f0e88246f66b09fe60f2a6e79562428c243b22cb;hpb=72c1f4c1dc6ab07fe121a327ff1c30bf51ef74c1;p=xscreensaver diff --git a/driver/demo-Gtk.c b/driver/demo-Gtk.c index f0e88246..0ff66e93 100644 --- a/driver/demo-Gtk.c +++ b/driver/demo-Gtk.c @@ -1,5 +1,5 @@ /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs. - * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski + * xscreensaver, Copyright (c) 1993-1999 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -54,10 +54,13 @@ # include "xmu.h" #endif - - #include +#ifdef HAVE_CRAPPLET +# include +# include +#endif + extern Display *gdk_display; #include "version.h" @@ -81,18 +84,18 @@ char *progname = 0; char *progclass = "XScreenSaver"; XrmDatabase db; +static Bool crapplet_p = False; +static Bool initializing_p; +static GtkWidget *toplevel_widget; + typedef struct { saver_preferences *a, *b; } prefs_pair; static void *global_prefs_pair; /* I hate C so much... */ - - char *blurb (void) { return progname; } -static void run_hack (int which); - static char *short_version = 0; Atom XA_VROOT; @@ -101,11 +104,12 @@ Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO; Atom XA_ACTIVATE, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT; - static void populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair); static void populate_prefs_page (GtkWidget *top, prefs_pair *pair); - +static int apply_changes_and_save (GtkWidget *widget); +static int maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair); +static void await_xscreensaver (GtkWidget *widget); /* Some random utility functions @@ -114,21 +118,10 @@ static void populate_prefs_page (GtkWidget *top, prefs_pair *pair); static GtkWidget * name_to_widget (GtkWidget *widget, const char *name) { - while (1) - { - GtkWidget *parent = (GTK_IS_MENU (widget) - ? gtk_menu_get_attach_widget (GTK_MENU (widget)) - : widget->parent); - if (parent) - widget = parent; - else - break; - } - return (GtkWidget *) gtk_object_get_data (GTK_OBJECT (widget), name); + return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name); } - /* Why this behavior isn't automatic in *either* toolkit, I'll never know. Takes a scroller, viewport, or list as an argument. */ @@ -179,7 +172,7 @@ ensure_selected_item_visible (GtkWidget *widget) kids; kids = kids->next) nkids++; - adj = gtk_scrolled_window_get_vadjustment (scroller); + adj = gtk_scrolled_window_get_vadjustment (scroller); gdk_window_get_geometry (GTK_WIDGET(vp)->window, &ignore, &ignore, &ignore, &parent_h, &ignore); @@ -190,6 +183,9 @@ ensure_selected_item_visible (GtkWidget *widget) ratio_t = ((double) child_y) / ((double) children_h); ratio_b = ((double) child_y + child_h) / ((double) children_h); + if (adj->upper == 0.0) /* no items in list */ + return; + if (ratio_t < (adj->value / adj->upper) || ratio_b > ((adj->value + adj->page_size) / adj->upper)) { @@ -218,9 +214,8 @@ ensure_selected_item_visible (GtkWidget *widget) } } - static void -warning_dialog_dismiss_cb (GtkButton *button, gpointer user_data) +warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data) { GtkWidget *shell = GTK_WIDGET (user_data); while (shell->parent) @@ -229,8 +224,17 @@ warning_dialog_dismiss_cb (GtkButton *button, gpointer user_data) } +void restart_menu_cb (GtkWidget *widget, gpointer user_data); + +static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data) +{ + restart_menu_cb (widget, user_data); + warning_dialog_dismiss_cb (widget, user_data); +} + static void -warning_dialog (GtkWidget *parent, const char *message, int center) +warning_dialog (GtkWidget *parent, const char *message, + Boolean restart_button_p, int center) { char *msg = strdup (message); char *head; @@ -238,6 +242,7 @@ warning_dialog (GtkWidget *parent, const char *message, int center) GtkWidget *dialog = gtk_dialog_new (); GtkWidget *label = 0; GtkWidget *ok = 0; + GtkWidget *cancel = 0; int i = 0; while (parent->parent) @@ -253,12 +258,16 @@ warning_dialog (GtkWidget *parent, const char *message, int center) sprintf (name, "label%d", i++); { +#if 0 char buf[255]; +#endif label = gtk_label_new (head); +#if 0 sprintf (buf, "warning_dialog.%s.font", name); GTK_WIDGET (label)->style = gtk_style_copy (GTK_WIDGET (label)->style); GTK_WIDGET (label)->style->font = gdk_font_load (get_string_resource (buf, "Dialog.Label.Font")); +#endif if (center <= 0) gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), @@ -283,22 +292,40 @@ warning_dialog (GtkWidget *parent, const char *message, int center) gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), label, TRUE, TRUE, 0); - ok = gtk_button_new_with_label ( - get_string_resource ("warning_dialog.ok.label", - "warning_dialog.Button.Label")); + ok = gtk_button_new_with_label ("OK"); gtk_container_add (GTK_CONTAINER (label), ok); + if (restart_button_p) + { + cancel = gtk_button_new_with_label ("Cancel"); + gtk_container_add (GTK_CONTAINER (label), cancel); + } + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_container_set_border_width (GTK_CONTAINER (dialog), 10); gtk_window_set_title (GTK_WINDOW (dialog), progclass); gtk_widget_show (ok); + if (cancel) + gtk_widget_show (cancel); gtk_widget_show (label); gtk_widget_show (dialog); /* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/ - gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", - GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), - (gpointer) dialog); + if (restart_button_p) + { + gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", + GTK_SIGNAL_FUNC (warning_dialog_restart_cb), + (gpointer) dialog); + gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked", + GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), + (gpointer) dialog); + } + else + { + gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", + GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb), + (gpointer) dialog); + } gdk_window_set_transient_for (GTK_WIDGET (dialog)->window, GTK_WIDGET (parent)->window); @@ -310,20 +337,38 @@ warning_dialog (GtkWidget *parent, const char *message, int center) static void -run_hack (int which) +run_cmd (GtkWidget *widget, Atom command, int arg) { + char *err = 0; int status; - if (which < 0) return; - status = xscreensaver_command (gdk_display, XA_DEMO, which + 1, False); + + apply_changes_and_save (widget); + status = xscreensaver_command (gdk_display, command, arg, False, &err); if (status < 0) { char buf [255]; - sprintf (buf, - "Error:\n\n" - "The DEMO %d command failed (%d).\n", which + 1, status); -#if 0 - warning_dialog (GTK_WIDGET (menuitem), buf, 1); -#endif + if (err) + sprintf (buf, "Error:\n\n%s", err); + else + strcpy (buf, "Unknown error!"); + warning_dialog (widget, buf, False, 100); + } + if (err) free (err); +} + + +static void +run_hack (GtkWidget *widget, int which, Bool report_errors_p) +{ + if (which < 0) return; + apply_changes_and_save (widget); + if (report_errors_p) + run_cmd (widget, XA_DEMO, which + 1); + else + { + char *s = 0; + xscreensaver_command (gdk_display, XA_DEMO, which + 1, False, &s); + if (s) free (s); } } @@ -335,13 +380,15 @@ run_hack (int which) void exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { + apply_changes_and_save (GTK_WIDGET (menuitem)); gtk_main_quit (); } static void wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { - exit_menu_cb (NULL, NULL); + apply_changes_and_save (widget); + gtk_main_quit (); } @@ -351,7 +398,7 @@ cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data) /* #### */ warning_dialog (GTK_WIDGET (menuitem), "Error:\n\n" - "cut unimplemented\n", 1); + "cut unimplemented\n", False, 1); } @@ -361,7 +408,7 @@ copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data) /* #### */ warning_dialog (GTK_WIDGET (menuitem), "Error:\n\n" - "copy unimplemented\n", 1); + "copy unimplemented\n", False, 1); } @@ -371,7 +418,7 @@ paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data) /* #### */ warning_dialog (GTK_WIDGET (menuitem), "Error:\n\n" - "paste unimplemented\n", 1); + "paste unimplemented\n", False, 1); } @@ -391,7 +438,7 @@ about_menu_cb (GtkMenuItem *menuitem, gpointer user_data) s, s2); free (s); - warning_dialog (GTK_WIDGET (menuitem), buf, 100); + warning_dialog (GTK_WIDGET (menuitem), buf, False, 100); } @@ -408,7 +455,7 @@ doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { warning_dialog (GTK_WIDGET (menuitem), "Error:\n\n" - "No Help URL has been specified.\n", 100); + "No Help URL has been specified.\n", False, 100); return; } @@ -426,72 +473,107 @@ doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data) void activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { - int status = xscreensaver_command (gdk_display, XA_ACTIVATE, 0, False); - if (status < 0) - { - char buf [255]; - sprintf (buf, - "Error:\n\n" - "The ACTIVATE command failed (%d).\n", status); - warning_dialog (GTK_WIDGET (menuitem), buf, 100); - } + run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0); } void lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { - int status = xscreensaver_command (gdk_display, XA_LOCK, 0, False); - if (status < 0) - { - char buf [255]; - sprintf (buf, - "Error:\n\n" - "The LOCK command failed (%d).\n", status); - warning_dialog (GTK_WIDGET (menuitem), buf, 100); - } + run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0); } void kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data) { - int status = xscreensaver_command (gdk_display, XA_EXIT, 0, False); - if (status < 0) - { - char buf [255]; - sprintf (buf, - "Error:\n\n" - "The EXIT command failed (%d).\n", status); - warning_dialog (GTK_WIDGET (menuitem), buf, 100); - } + run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0); } void -restart_menu_cb (GtkMenuItem *menuitem, gpointer user_data) +restart_menu_cb (GtkWidget *widget, gpointer user_data) { #if 0 - int status = xscreensaver_command (gdk_display, XA_RESTART, 0, False); - if (status < 0) - { - char buf [255]; - sprintf (buf, - "Error:\n\n" - "The RESTART command failed (%d).\n", status); - warning_dialog (GTK_WIDGET (menuitem), buf, 100); - } + run_cmd (GTK_WIDGET (widget), XA_RESTART, 0); #else - xscreensaver_command (gdk_display, XA_EXIT, 0, False); + apply_changes_and_save (GTK_WIDGET (widget)); + xscreensaver_command (gdk_display, XA_EXIT, 0, False, NULL); sleep (1); system ("xscreensaver -nosplash &"); #endif + + await_xscreensaver (GTK_WIDGET (widget)); } +static void +await_xscreensaver (GtkWidget *widget) +{ + int countdown = 5; + + Display *dpy = gdk_display; + /* GtkWidget *dialog = 0;*/ + char *rversion = 0; + + while (!rversion && (--countdown > 0)) + { + /* Check for the version of the running xscreensaver... */ + server_xscreensaver_version (dpy, &rversion, 0, 0); + + /* If it's not there yet, wait a second... */ + sleep (1); + } + +/* if (dialog) gtk_widget_destroy (dialog);*/ + + if (rversion) + { + /* Got it. */ + free (rversion); + } + else + { + /* Timed out, no screensaver running. */ + + char buf [1024]; + Bool root_p = (geteuid () == 0); + + strcpy (buf, + "Error:\n\n" + "The xscreensaver daemon did not start up properly.\n" + "\n"); + + if (root_p) + strcat (buf, + "You are running as root. This usually means that xscreensaver\n" + "was unable to contact your X server because access control is\n" + "turned on. Try running this command:\n" + "\n" + " xhost +localhost\n" + "\n" + "and then selecting `File / Restart Daemon'.\n" + "\n" + "Note that turning off access control will allow anyone logged\n" + "on to this machine to access your screen, which might be\n" + "considered a security problem. Please read the xscreensaver\n" + "manual and FAQ for more information.\n" + "\n" + "You shouldn't run X as root. Instead, you should log in as a\n" + "normal user, and `su' as necessary."); + else + strcat (buf, "Please check your $PATH and permissions."); + + warning_dialog (widget, buf, False, 1); + } +} + + +static int _selected_hack_number = -1; static int selected_hack_number (GtkWidget *toplevel) { +#if 0 GtkViewport *vp = GTK_VIEWPORT (name_to_widget (toplevel, "viewport")); GtkList *list_widget = GTK_LIST (GTK_BIN(vp)->child); GList *slist = list_widget->selection; @@ -500,25 +582,50 @@ selected_hack_number (GtkWidget *toplevel) ? gtk_list_child_position (list_widget, GTK_WIDGET (selected)) : -1); return which; +#else + return _selected_hack_number; +#endif } -void -apply_this_cb (GtkButton *button, gpointer user_data) +static int +demo_write_init_file (GtkWidget *widget, saver_preferences *p) +{ + if (!write_init_file (p, short_version, False)) + return 0; + else + { + const char *f = init_file_name(); + if (!f || !*f) + warning_dialog (widget, + "Error:\n\nCouldn't determine init file name!\n", + False, 100); + else + { + char *b = (char *) malloc (strlen(f) + 1024); + sprintf (b, "Error:\n\nCouldn't write %s\n", f); + warning_dialog (widget, b, False, 100); + free (b); + } + return -1; + } +} + + +static int +apply_changes_and_save_1 (GtkWidget *widget) { /* prefs_pair *pair = (prefs_pair *) client_data; */ prefs_pair *pair = global_prefs_pair; /* I hate C so much... */ - saver_preferences *p = pair->a; GtkList *list_widget = - GTK_LIST (name_to_widget (GTK_WIDGET (button), "list")); - int which = selected_hack_number (GTK_WIDGET (button)); + GTK_LIST (name_to_widget (widget, "list")); + int which = selected_hack_number (widget); - GtkEntry *cmd = GTK_ENTRY (name_to_widget (GTK_WIDGET (button), "cmd_text")); + GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text")); GtkToggleButton *enabled = - GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET (button), "enabled")); - GtkCombo *vis = GTK_COMBO (name_to_widget (GTK_WIDGET (button), - "visual_combo")); + GTK_TOGGLE_BUTTON (name_to_widget (widget, "enabled")); + GtkCombo *vis = GTK_COMBO (name_to_widget (widget, "visual_combo")); Bool enabled_p = gtk_toggle_button_get_active (enabled); const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry)); @@ -527,12 +634,16 @@ apply_this_cb (GtkButton *button, gpointer user_data) char c; unsigned long id; - if (which < 0) return; + if (which < 0) return -1; + + if (maybe_reload_init_file (widget, pair) != 0) + return 1; /* Sanity-check and canonicalize whatever the user typed into the combo box. */ - if (!strcasecmp (visual, "any")) visual = ""; - if (!strcasecmp (visual, "default")) visual = "Default"; + if (!strcasecmp (visual, "")) visual = ""; + else if (!strcasecmp (visual, "any")) visual = ""; + else if (!strcasecmp (visual, "default")) visual = "Default"; else if (!strcasecmp (visual, "default-n")) visual = "Default-N"; else if (!strcasecmp (visual, "default-i")) visual = "Default-I"; else if (!strcasecmp (visual, "best")) visual = "Best"; @@ -558,17 +669,40 @@ apply_this_cb (GtkButton *button, gpointer user_data) gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any"); } - p->screenhacks[which]->enabled_p = enabled_p; - if (p->screenhacks[which]->visual) - free (p->screenhacks[which]->visual); - if (p->screenhacks[which]->command) - free (p->screenhacks[which]->command); - p->screenhacks[which]->visual = strdup (visual); - p->screenhacks[which]->command = strdup (command); - ensure_selected_item_visible (GTK_WIDGET (list_widget)); - write_init_file (p, short_version); + if (!p->screenhacks[which]->visual) + p->screenhacks[which]->visual = strdup (""); + if (!p->screenhacks[which]->command) + p->screenhacks[which]->command = strdup (""); + + if (p->screenhacks[which]->enabled_p != enabled_p || + !!strcasecmp (p->screenhacks[which]->visual, visual) || + !!strcasecmp (p->screenhacks[which]->command, command)) + { + /* Something was changed -- store results into the struct, + and write the file. + */ + free (p->screenhacks[which]->visual); + free (p->screenhacks[which]->command); + p->screenhacks[which]->visual = strdup (visual); + p->screenhacks[which]->command = strdup (command); + p->screenhacks[which]->enabled_p = enabled_p; + + return demo_write_init_file (widget, p); + } + + /* No changes made */ + return 0; +} + +void prefs_ok_cb (GtkButton *button, gpointer user_data); + +static int +apply_changes_and_save (GtkWidget *widget) +{ + prefs_ok_cb ((GtkButton *) widget, 0); + return apply_changes_and_save_1 (widget); } @@ -577,22 +711,54 @@ run_this_cb (GtkButton *button, gpointer user_data) { int which = selected_hack_number (GTK_WIDGET (button)); if (which < 0) return; - apply_this_cb (button, user_data); - run_hack (which); + if (0 == apply_changes_and_save (GTK_WIDGET (button))) + run_hack (GTK_WIDGET (button), which, True); } void -cancel_this_cb (GtkButton *button, gpointer user_data) +manual_cb (GtkButton *button, gpointer user_data) { /* prefs_pair *pair = (prefs_pair *) client_data; */ prefs_pair *pair = global_prefs_pair; /* I hate C so much... */ + saver_preferences *p = pair->a; GtkList *list_widget = GTK_LIST (name_to_widget (GTK_WIDGET (button), "list")); int which = selected_hack_number (GTK_WIDGET (button)); + char *name, *name2, *cmd, *s; if (which < 0) return; + apply_changes_and_save (GTK_WIDGET (button)); ensure_selected_item_visible (GTK_WIDGET (list_widget)); - populate_demo_window (GTK_WIDGET (button), which, pair); + + name = strdup (p->screenhacks[which]->command); + name2 = name; + while (isspace (*name2)) name2++; + s = name2; + while (*s && !isspace (*s)) s++; + *s = 0; + s = strrchr (name2, '/'); + if (s) name = s+1; + + cmd = get_string_resource ("manualCommand", "ManualCommand"); + if (cmd) + { + char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100); + strcpy (cmd2, "( "); + sprintf (cmd2 + strlen (cmd2), + cmd, + name2, name2, name2, name2); + strcat (cmd2, " ) &"); + system (cmd2); + free (cmd2); + } + else + { + warning_dialog (GTK_WIDGET (button), + "Error:\n\nno `manualCommand' resource set.", + False, 100); + } + + free (name); } @@ -615,10 +781,11 @@ run_next_cb (GtkButton *button, gpointer user_data) if (which >= p->screenhacks_count) which = 0; + apply_changes_and_save (GTK_WIDGET (button)); gtk_list_select_item (GTK_LIST (list_widget), which); ensure_selected_item_visible (GTK_WIDGET (list_widget)); populate_demo_window (GTK_WIDGET (button), which, pair); - run_hack (which); + run_hack (GTK_WIDGET (button), which, False); } @@ -641,10 +808,11 @@ run_prev_cb (GtkButton *button, gpointer user_data) if (which < 0) which = p->screenhacks_count - 1; + apply_changes_and_save (GTK_WIDGET (button)); gtk_list_select_item (GTK_LIST (list_widget), which); ensure_selected_item_visible (GTK_WIDGET (list_widget)); populate_demo_window (GTK_WIDGET (button), which, pair); - run_hack (which); + run_hack (GTK_WIDGET (button), which, False); } @@ -652,7 +820,7 @@ run_prev_cb (GtkButton *button, gpointer user_data) this parses the text, and does error checking. */ static void -hack_time_text (const char *line, Time *store, Bool sec_p) +hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p) { if (*line) { @@ -660,7 +828,14 @@ hack_time_text (const char *line, Time *store, Bool sec_p) value = parse_time ((char *) line, sec_p, True); value *= 1000; /* Time measures in microseconds */ if (value < 0) - /* gdk_beep () */; + { + char b[255]; + sprintf (b, + "Error:\n\n" + "Unparsable time format: \"%s\"\n", + line); + warning_dialog (widget, b, False, 100); + } else *store = value; } @@ -675,15 +850,16 @@ prefs_ok_cb (GtkButton *button, gpointer user_data) saver_preferences *p = pair->a; saver_preferences *p2 = pair->b; + Bool changed = False; # define SECONDS(field, name) \ - hack_time_text (gtk_entry_get_text (\ + hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\ GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \ (field), \ True) # define MINUTES(field, name) \ - hack_time_text (gtk_entry_get_text (\ + hack_time_text (GTK_WIDGET(button), gtk_entry_get_text (\ GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \ (field), \ False) @@ -696,37 +872,57 @@ prefs_ok_cb (GtkButton *button, gpointer user_data) if (! *line) \ ; \ else if (sscanf (line, "%u%c", &value, &c) != 1) \ - gdk_beep(); \ + { \ + char b[255]; \ + sprintf (b, "Error:\n\n" "Not an integer: \"%s\"\n", line); \ + warning_dialog (GTK_WIDGET (button), b, False, 100); \ + } \ else \ *(field) = value; \ } while(0) +# define CHECKBOX(field, name) \ + field = gtk_toggle_button_get_active (\ + GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name)))) + MINUTES (&p2->timeout, "timeout_text"); MINUTES (&p2->cycle, "cycle_text"); SECONDS (&p2->fade_seconds, "fade_text"); INTEGER (&p2->fade_ticks, "ticks_text"); MINUTES (&p2->lock_timeout, "lock_text"); SECONDS (&p2->passwd_timeout, "pass_text"); - -#undef SECONDS -#undef MINUTES -#undef INTEGER - - p->timeout = p2->timeout; - p->cycle = p2->cycle; - p->lock_timeout = p2->lock_timeout; - p->passwd_timeout = p2->passwd_timeout; - p->fade_seconds = p2->fade_seconds; - p->fade_ticks = p2->fade_ticks; - p->verbose_p = p2->verbose_p; - p->install_cmap_p = p2->install_cmap_p; - p->fade_p = p2->fade_p; - p->unfade_p = p2->unfade_p; - p->lock_p = p2->lock_p; + CHECKBOX (p2->verbose_p, "verbose_button"); + CHECKBOX (p2->install_cmap_p, "install_button"); + CHECKBOX (p2->fade_p, "fade_button"); + CHECKBOX (p2->unfade_p, "unfade_button"); + CHECKBOX (p2->lock_p, "lock_button"); + +# undef SECONDS +# undef MINUTES +# undef INTEGER +# undef CHECKBOX + +# define COPY(field) \ + if (p->field != p2->field) changed = True; \ + p->field = p2->field + + COPY(timeout); + COPY(cycle); + COPY(lock_timeout); + COPY(passwd_timeout); + COPY(fade_seconds); + COPY(fade_ticks); + COPY(verbose_p); + COPY(install_cmap_p); + COPY(fade_p); + COPY(unfade_p); + COPY(lock_p); +# undef COPY populate_prefs_page (GTK_WIDGET (button), pair); - write_init_file (p, short_version); + if (changed) + demo_write_init_file (GTK_WIDGET (button), p); } @@ -741,6 +937,14 @@ prefs_cancel_cb (GtkButton *button, gpointer user_data) } +void +pref_changed_cb (GtkButton *button, gpointer user_data) +{ + if (! initializing_p) + apply_changes_and_save (GTK_WIDGET (button)); +} + + static gint list_doubleclick_cb (GtkWidget *button, GdkEventButton *event, gpointer client_data) @@ -751,7 +955,7 @@ list_doubleclick_cb (GtkWidget *button, GdkEventButton *event, int which = gtk_list_child_position (list, GTK_WIDGET (button)); if (which >= 0) - run_hack (which); + run_hack (GTK_WIDGET (button), which, True); } return FALSE; @@ -765,6 +969,7 @@ list_select_cb (GtkList *list, GtkWidget *child) prefs_pair *pair = global_prefs_pair; /* I hate C so much... */ int which = gtk_list_child_position (list, GTK_WIDGET (child)); + apply_changes_and_save (GTK_WIDGET (list)); populate_demo_window (GTK_WIDGET (list), which, pair); } @@ -774,9 +979,84 @@ list_unselect_cb (GtkList *list, GtkWidget *child) /* prefs_pair *pair = (prefs_pair *) client_data; */ prefs_pair *pair = global_prefs_pair; /* I hate C so much... */ + apply_changes_and_save (GTK_WIDGET (list)); populate_demo_window (GTK_WIDGET (list), -1, pair); } + +static int updating_enabled_cb = 0; /* kludge to make sure that enabled_cb + is only run by user action, not by + program action. */ + +/* Called when the checkboxes that are in the left column of the + scrolling list are clicked. This both populates the right pane + (just as clicking on the label (really, listitem) does) and + also syncs this checkbox with the right pane Enabled checkbox. + */ +static void +list_checkbox_cb (GtkWidget *cb, gpointer client_data) +{ + prefs_pair *pair = (prefs_pair *) client_data; + + GtkWidget *line_hbox = GTK_WIDGET (cb)->parent; + GtkWidget *line = GTK_WIDGET (line_hbox)->parent; + + GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent); + GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent); + GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent); + GtkAdjustment *adj; + double scroll_top; + + GtkToggleButton *enabled = + GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled")); + + int which = gtk_list_child_position (list, line); + + /* remember previous scroll position of the top of the list */ + adj = gtk_scrolled_window_get_vadjustment (scroller); + scroll_top = adj->value; + + apply_changes_and_save (GTK_WIDGET (list)); + gtk_list_select_item (list, which); + /* ensure_selected_item_visible (GTK_WIDGET (list)); */ + populate_demo_window (GTK_WIDGET (list), which, pair); + + updating_enabled_cb++; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (enabled), + GTK_TOGGLE_BUTTON (cb)->active); + updating_enabled_cb--; + + /* restore the previous scroll position of the top of the list. + this is weak, but I don't really know why it's moving... */ + gtk_adjustment_set_value (adj, scroll_top); +} + + +/* Called when the right pane Enabled checkbox is clicked. This syncs + the corresponding checkbox inside the scrolling list to the state + of this checkbox. + */ +void +enabled_cb (GtkWidget *cb, gpointer client_data) +{ + int which = selected_hack_number (cb); + + if (updating_enabled_cb) return; + + if (which != -1) + { + GtkList *list = GTK_LIST (name_to_widget (cb, "list")); + GList *kids = GTK_LIST (list)->children; + GtkWidget *line = GTK_WIDGET (g_list_nth_data (kids, which)); + GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child); + GtkWidget *line_check = + GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check), + GTK_TOGGLE_BUTTON (cb)->active); + } +} + /* Populating the various widgets */ @@ -851,6 +1131,7 @@ make_pretty_name (const char *shell_command) static void scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair) { + saver_preferences *p = pair->a; Atom type; int format; unsigned long nitems, bytesafter; @@ -876,9 +1157,13 @@ scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair) return; list = GTK_LIST (name_to_widget (toplevel, "list")); - gtk_list_select_item (list, which); - ensure_selected_item_visible (GTK_WIDGET (list)); - populate_demo_window (toplevel, which, pair); + apply_changes_and_save (toplevel); + if (which < p->screenhacks_count) + { + gtk_list_select_item (list, which); + ensure_selected_item_visible (GTK_WIDGET (list)); + populate_demo_window (toplevel, which, pair); + } } @@ -891,20 +1176,52 @@ populate_hack_list (GtkWidget *toplevel, prefs_pair *pair) screenhack **hacks = p->screenhacks; screenhack **h; - for (h = hacks; *h; h++) + for (h = hacks; h && *h; h++) { + /* A GtkList must contain only GtkListItems, but those can contain + an arbitrary widget. We add an Hbox, and inside that, a Checkbox + and a Label. We handle single and double click events on the + line itself, for clicking on the text, but the interior checkbox + also handles its own events. + */ GtkWidget *line; + GtkWidget *line_hbox; + GtkWidget *line_check; + GtkWidget *line_label; + char *pretty_name = (h[0]->name ? strdup (h[0]->name) : make_pretty_name (h[0]->command)); - line = gtk_list_item_new_with_label (pretty_name); + line = gtk_list_item_new (); + line_hbox = gtk_hbox_new (FALSE, 0); + line_check = gtk_check_button_new (); + line_label = gtk_label_new (pretty_name); + + gtk_container_add (GTK_CONTAINER (line), line_hbox); + gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check), + h[0]->enabled_p); + gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT); + + gtk_widget_show (line_check); + gtk_widget_show (line_label); + gtk_widget_show (line_hbox); + gtk_widget_show (line); + free (pretty_name); gtk_container_add (GTK_CONTAINER (list), line); gtk_signal_connect (GTK_OBJECT (line), "button_press_event", GTK_SIGNAL_FUNC (list_doubleclick_cb), (gpointer) pair); + + gtk_signal_connect (GTK_OBJECT (line_check), "toggled", + GTK_SIGNAL_FUNC (list_checkbox_cb), + (gpointer) pair); + #if 0 /* #### */ GTK_WIDGET (GTK_BIN(line)->child)->style = gtk_style_copy (GTK_WIDGET (text_line)->style); @@ -1003,7 +1320,7 @@ sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p) { const char *names[] = { "cmd_label", "cmd_text", "enabled", "visual", "visual_combo", - "demo", "apply", "cancel" }; + "demo", "manual" }; int i; for (i = 0; i < countof(names); i++) { @@ -1102,6 +1419,7 @@ static char *down_arrow_xpm[] = { "+ c #D6D6D6", "@ c #000000", + " ", " ------------- ", " -+++++++++++@ ", " -+++++++++@ ", @@ -1124,7 +1442,7 @@ static char *down_arrow_xpm[] = { }; static void -pixmapify_buttons (GtkWidget *toplevel) +pixmapify_button (GtkWidget *toplevel, int down_p) { GdkPixmap *pixmap; GdkBitmap *mask; @@ -1132,27 +1450,31 @@ pixmapify_buttons (GtkWidget *toplevel) GtkStyle *style; GtkWidget *w; - w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "next")); + w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), + (down_p ? "next" : "prev"))); style = gtk_widget_get_style (w); mask = 0; pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask, &style->bg[GTK_STATE_NORMAL], - (gchar **) down_arrow_xpm); + (down_p + ? (gchar **) down_arrow_xpm + : (gchar **) up_arrow_xpm)); pixmapwid = gtk_pixmap_new (pixmap, mask); gtk_widget_show (pixmapwid); gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child); gtk_container_add (GTK_CONTAINER (w), pixmapwid); +} - w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "prev")); - style = gtk_widget_get_style (w); - mask = 0; - pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask, - &style->bg[GTK_STATE_NORMAL], - (gchar **) up_arrow_xpm); - pixmapwid = gtk_pixmap_new (pixmap, mask); - gtk_widget_show (pixmapwid); - gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child); - gtk_container_add (GTK_CONTAINER (w), pixmapwid); +static void +map_next_button_cb (GtkWidget *w, gpointer user_data) +{ + pixmapify_button (w, 1); +} + +static void +map_prev_button_cb (GtkWidget *w, gpointer user_data) +{ + pixmapify_button (w, 0); } @@ -1302,7 +1624,8 @@ static void populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair) { saver_preferences *p = pair->a; - screenhack *hack = (which >= 0 ? p->screenhacks[which] : 0); + screenhack *hack = (which >= 0 && which < p->screenhacks_count + ? p->screenhacks[which] : 0); GtkFrame *frame = GTK_FRAME (name_to_widget (toplevel, "frame")); GtkLabel *doc = GTK_LABEL (name_to_widget (toplevel, "doc")); GtkEntry *cmd = GTK_ENTRY (name_to_widget (toplevel, "cmd_text")); @@ -1321,10 +1644,14 @@ populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair) gtk_label_set_text (doc, (doc_string ? doc_string : "")); gtk_entry_set_text (cmd, (hack ? hack->command : "")); gtk_entry_set_position (cmd, 0); + + updating_enabled_cb++; gtk_toggle_button_set_active (enabled, (hack ? hack->enabled_p : False)); + updating_enabled_cb--; + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), (hack - ? (hack->visual && hack->visual + ? (hack->visual && *hack->visual ? hack->visual : "Any") : "")); @@ -1335,9 +1662,74 @@ populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair) if (pretty_name) free (pretty_name); if (doc_string) free (doc_string); + + _selected_hack_number = which; } +static void +widget_deleter (GtkWidget *widget, gpointer data) +{ + /* #### Well, I want to destroy these widgets, but if I do that, they get + referenced again, and eventually I get a SEGV. So instead of + destroying them, I'll just hide them, and leak a bunch of memory + every time the disk file changes. Go go go Gtk! + + #### Ok, that's a lie, I get a crash even if I just hide the widget + and don't ever delete it. Fuck! + */ +#if 0 + gtk_widget_destroy (widget); +#else + gtk_widget_hide (widget); +#endif +} + + +static int +maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair) +{ + int status = 0; + saver_preferences *p = pair->a; + + static Bool reentrant_lock = False; + if (reentrant_lock) return 0; + reentrant_lock = True; + + if (init_file_changed_p (p)) + { + const char *f = init_file_name(); + char *b; + int which; + GtkList *list; + + if (!f || !*f) return 0; + b = (char *) malloc (strlen(f) + 1024); + sprintf (b, + "Warning:\n\n" + "file \"%s\" has changed, reloading.\n", + f); + warning_dialog (widget, b, False, 100); + free (b); + + load_init_file (p); + + which = selected_hack_number (widget); + list = GTK_LIST (name_to_widget (widget, "list")); + gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL); + populate_hack_list (widget, pair); + gtk_list_select_item (list, which); + populate_prefs_page (widget, pair); + populate_demo_window (widget, which, pair); + ensure_selected_item_visible (GTK_WIDGET (list)); + + status = 1; + } + + reentrant_lock = False; + return status; +} + /* The main demo-mode command loop. @@ -1412,9 +1804,7 @@ the_network_is_not_the_computer (GtkWidget *parent) sprintf (msg, "Warning:\n\n" "The XScreenSaver daemon doesn't seem to be running\n" - "on display \"%s\". You can launch it by selecting\n" - "`Restart Daemon' from the File menu, or by typing\n" - "\"xscreensaver &\" in a shell.", + "on display \"%s\". Launch it now?", d); } else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name)) @@ -1431,9 +1821,10 @@ the_network_is_not_the_computer (GtkWidget *parent) "the same ~/.xscreensaver file, so %s isn't\n" "going to work right.\n" "\n" - "Either re-run %s as \"%s\", or re-run\n" - "xscreensaver as \"%s\" (which you can do by\n" - "selecting `Restart Daemon' from the File menu.)\n", + "You should either re-run %s as \"%s\", or re-run\n" + "xscreensaver as \"%s\".\n" + "\n" + "Restart the xscreensaver daemon now?\n", progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), @@ -1455,8 +1846,7 @@ the_network_is_not_the_computer (GtkWidget *parent) "if they don't see the same ~%s/.xscreensaver file) then\n" "%s won't work right.\n" "\n" - "You can restart the daemon on \"%s\" as \"%s\" by\n" - "selecting `Restart Daemon' from the File menu.)", + "Restart the daemon on \"%s\" as \"%s\" now?\n", progname, luser, lhost, d, (ruser ? ruser : "???"), (rhost ? rhost : "???"), @@ -1472,7 +1862,9 @@ the_network_is_not_the_computer (GtkWidget *parent) "Warning:\n\n" "This is %s version %s.\n" "But the xscreensaver managing display \"%s\"\n" - "is version %s. This could cause problems.", + "is version %s. This could cause problems.\n" + "\n" + "Restart the xscreensaver daemon now?\n", progname, short_version, d, rversion); @@ -1480,7 +1872,7 @@ the_network_is_not_the_computer (GtkWidget *parent) if (*msg) - warning_dialog (parent, msg, 1); + warning_dialog (parent, msg, True, 1); free (msg); } @@ -1536,6 +1928,30 @@ static char *defaults[] = { 0 }; +#if 0 +#ifdef HAVE_CRAPPLET +static struct poptOption crapplet_options[] = { + {NULL, '\0', 0, NULL, 0} +}; +#endif /* HAVE_CRAPPLET */ +#endif /* 0 */ + +#define USAGE() \ + fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \ + real_progname) + + +static void +map_window_cb (GtkWidget *w, gpointer user_data) +{ + Boolean oi = initializing_p; + initializing_p = True; + eschew_gtk_lossage (w); + ensure_selected_item_visible (GTK_WIDGET(name_to_widget(w, "list"))); + initializing_p = oi; +} + + int main (int argc, char **argv) { @@ -1550,6 +1966,8 @@ main (int argc, char **argv) char *real_progname = argv[0]; char *s; + initializing_p = True; + s = strrchr (real_progname, '/'); if (s) real_progname = s+1; @@ -1588,9 +2006,104 @@ main (int argc, char **argv) !strncmp(argv[i], "-display", strlen(argv[i]))) argv[i] = "--display"; + + /* We need to parse this arg really early... Sigh. */ + for (i = 1; i < argc; i++) + if (argv[i] && + (!strcmp(argv[i], "--crapplet") || + !strcmp(argv[i], "--capplet"))) + { +# ifdef HAVE_CRAPPLET + int j; + crapplet_p = True; + for (j = i; j < argc; j++) /* remove it from the list */ + argv[j] = argv[j+1]; + argc--; + +# else /* !HAVE_CRAPPLET */ + fprintf (stderr, "%s: not compiled with --crapplet support\n", + real_progname[i]); + USAGE (); + exit (1); +# endif /* !HAVE_CRAPPLET */ + } + /* Let Gtk open the X connection, then initialize Xt to use that - same connection. Doctor Frankenstein would be proud. */ - gtk_init (&argc, &argv); + same connection. Doctor Frankenstein would be proud. + */ +# ifdef HAVE_CRAPPLET + if (crapplet_p) + { + GnomeClient *client; + GnomeClientFlags flags = 0; + + int init_results = gnome_capplet_init ("screensaver-properties", + short_version, + argc, argv, NULL, 0, NULL); + /* init_results is: + 0 upon successful initialization; + 1 if --init-session-settings was passed on the cmdline; + 2 if --ignore was passed on the cmdline; + -1 on error. + + So the 1 signifies just to init the settings, and quit, basically. + (Meaning launch the xscreensaver daemon.) + */ + + if (init_results < 0) + { +# if 0 + g_error ("An initialization error occurred while " + "starting xscreensaver-capplet.\n"); +# else /* !0 */ + fprintf (stderr, "%s: gnome_capplet_init failed: %d\n", + real_progname, init_results); + exit (1); +# endif /* !0 */ + } + + client = gnome_master_client (); + + if (client) + flags = gnome_client_get_flags (client); + + if (flags & GNOME_CLIENT_IS_CONNECTED) + { + int token = + gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES", + gnome_client_get_id (client)); + if (token) + { + char *session_args[20]; + int i = 0; + session_args[i++] = real_progname; + session_args[i++] = "--capplet"; + session_args[i++] = "--init-session-settings"; + session_args[i] = 0; + gnome_client_set_priority (client, 20); + gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY); + gnome_client_set_restart_command (client, i, session_args); + } + else + { + gnome_client_set_restart_style (client, GNOME_RESTART_NEVER); + } + + gnome_client_flush (client); + } + + if (init_results == 1) + { + system ("xscreensaver -nosplash &"); + return 0; + } + + } + else +# endif /* HAVE_CRAPPLET */ + { + gtk_init (&argc, &argv); + } /* We must read exactly the same resources as xscreensaver. @@ -1629,11 +2142,15 @@ main (int argc, char **argv) s++; if (!strcmp (s, "-prefs")) prefs = True; + else if (crapplet_p) + /* There are lots of random args that we don't care about when we're + started as a crapplet, so just ignore unknown args in that case. */ + ; else { - fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", - real_progname); - exit (1); + fprintf (stderr, "%s: unknown option: %s\n", real_progname, argv[i]); + USAGE (); + exit (1); } } @@ -1685,6 +2202,7 @@ main (int argc, char **argv) /* Create the window and all its widgets. */ gtk_window = create_xscreensaver_demo (); + toplevel_widget = gtk_window; /* Set the window's title. */ { @@ -1712,12 +2230,17 @@ main (int argc, char **argv) sensitize_demo_widgets (gtk_window, False); fix_text_entry_sizes (gtk_window); scroll_to_current_hack (gtk_window, pair); - gtk_widget_show (gtk_window); - /* The next three calls must come after gtk_widget_show(). */ - pixmapify_buttons (gtk_window); - eschew_gtk_lossage (gtk_window); - ensure_selected_item_visible (GTK_WIDGET(name_to_widget(gtk_window,"list"))); + gtk_signal_connect ( + GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "list")), + "map", GTK_SIGNAL_FUNC(map_window_cb), 0); + gtk_signal_connect ( + GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "prev")), + "map", GTK_SIGNAL_FUNC(map_prev_button_cb), 0); + gtk_signal_connect ( + GTK_OBJECT (name_to_widget (GTK_WIDGET (gtk_window), "next")), + "map", GTK_SIGNAL_FUNC(map_next_button_cb), 0); + /* Handle the -prefs command-line argument. */ if (prefs) @@ -1727,8 +2250,42 @@ main (int argc, char **argv) gtk_notebook_set_page (notebook, 1); } - /* Issue any warnings about the running xscreensaver daemon. */ - the_network_is_not_the_computer (gtk_window); +# ifdef HAVE_CRAPPLET + if (crapplet_p) + { + GtkWidget *capplet; + GtkWidget *top_vbox; + + capplet = capplet_widget_new (); + + top_vbox = GTK_BIN (gtk_window)->child; + + gtk_widget_ref (top_vbox); + gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox); + GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING); + + /* In crapplet-mode, take off the menubar. */ + gtk_widget_hide (name_to_widget (gtk_window, "menubar")); + + gtk_container_add (GTK_CONTAINER (capplet), top_vbox); + gtk_widget_show (capplet); + gtk_widget_hide (gtk_window); + + /* Hook up the Control Center's redundant Help button, too. */ + gtk_signal_connect (GTK_OBJECT (capplet), "help", + GTK_SIGNAL_FUNC (doc_menu_cb), 0); + + /* Issue any warnings about the running xscreensaver daemon. */ + the_network_is_not_the_computer (top_vbox); + } + else +# endif /* HAVE_CRAPPLET */ + { + gtk_widget_show (gtk_window); + + /* Issue any warnings about the running xscreensaver daemon. */ + the_network_is_not_the_computer (gtk_window); + } /* Run the Gtk event loop, and not the Xt event loop. This means that if there were Xt timers or fds registered, they would never get serviced, @@ -1737,7 +2294,15 @@ main (int argc, char **argv) Xt so that we could process the command line and use the X resource manager. */ - gtk_main (); + initializing_p = False; + +# ifdef HAVE_CRAPPLET + if (crapplet_p) + capplet_gtk_main (); + else +# endif /* HAVE_CRAPPLET */ + gtk_main (); + exit (0); }