/* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
- * xscreensaver, Copyright (c) 1993-2001 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1993-2013 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#ifdef HAVE_GTK /* whole file */
+#include <xscreensaver-intl.h>
+
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
+# ifdef __GNUC__
+# define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
+# else
+# define STFU /* */
+# endif
+
+
+#ifdef ENABLE_NLS
+# include <locale.h>
+#endif /* ENABLE_NLS */
+
#ifndef VMS
# include <pwd.h> /* for getpwuid() */
#else /* VMS */
#endif /* HAVE_UNAME */
#include <stdio.h>
+#include <time.h>
#include <sys/stat.h>
+#include <sys/time.h>
+
+
+#include <signal.h>
+#include <errno.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h> /* for waitpid() and associated macros */
+#endif
+
#include <X11/Xproto.h> /* for CARD32 */
#include <X11/Xatom.h> /* for XA_INTEGER */
# include "xmu.h"
#endif
+#ifdef HAVE_XINERAMA
+# include <X11/extensions/Xinerama.h>
+#endif /* HAVE_XINERAMA */
+
#include <gtk/gtk.h>
#ifdef HAVE_CRAPPLET
#include <gdk/gdkx.h>
+#ifdef HAVE_GTK2
+# include <glade/glade-xml.h>
+# include <gmodule.h>
+#else /* !HAVE_GTK2 */
+# define G_MODULE_EXPORT /**/
+#endif /* !HAVE_GTK2 */
+
+#if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
+# define GLADE_DIR DEFAULT_ICONDIR
+#endif
+#if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
+# define DEFAULT_ICONDIR GLADE_DIR
+#endif
+
+#ifndef HAVE_XML
+ /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
+ It is unused otherwise, so in that case, stub it out. */
+ static const char *hack_configuration_path = 0;
+#endif
+
+
+
#include "version.h"
#include "prefs.h"
#include "resources.h" /* for parse_time() */
#include "logo-50.xpm"
#include "logo-180.xpm"
-#include "demo-Gtk-widgets.h"
+#include "demo-Gtk-conf.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#ifdef HAVE_GTK2
+enum {
+ COL_ENABLED,
+ COL_NAME,
+ COL_LAST
+};
+#endif /* HAVE_GTK2 */
+
+/* Deal with deprecation of direct access to struct fields on the way to GTK3
+ See http://live.gnome.org/GnomeGoals/UseGseal
+ */
+#if GTK_CHECK_VERSION(2,14,0)
+# define GET_PARENT(w) gtk_widget_get_parent (w)
+# define GET_WINDOW(w) gtk_widget_get_window (w)
+# define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
+# define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
+# define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
+# define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
+# define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
+#else
+# define GET_PARENT(w) ((w)->parent)
+# define GET_WINDOW(w) ((w)->window)
+# define GET_ACTION_AREA(d) ((d)->action_area)
+# define GET_CONTENT_AREA(d) ((d)->vbox)
+# define GET_ADJ_VALUE(a) ((a)->value)
+# define SET_ADJ_VALUE(a,v) (a)->value = v
+# define SET_ADJ_UPPER(a,v) (a)->upper = v
+#endif
+
+#if GTK_CHECK_VERSION(2,18,0)
+# define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
+# define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
+#else
+# define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
+# define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
+#endif
+
+#if GTK_CHECK_VERSION(2,20,0)
+# define GET_REALIZED(w) gtk_widget_get_realized (w)
+#else
+# define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
+#endif
+
+/* from exec.c */
+extern void exec_command (const char *shell, const char *command, int nice);
+extern int on_path_p (const char *program);
+
+static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
+
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
+/* You might think that to read an array of 32-bit quantities out of a
+ server-side property, you would pass an array of 32-bit data quantities
+ into XGetWindowProperty(). You would be wrong. You have to use an array
+ of longs, even if long is 64 bits (using 32 of each 64.)
+ */
+typedef long PROP32;
+
char *progname = 0;
char *progclass = "XScreenSaver";
XrmDatabase db;
-static Bool crapplet_p = False;
-static Bool initializing_p;
-static GtkWidget *toplevel_widget;
+/* The order of the items in the mode menu. */
+static int mode_menu_order[] = {
+ DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
+
typedef struct {
- saver_preferences *a, *b;
-} prefs_pair;
-static void *global_prefs_pair; /* I hate C so much... */
+ char *short_version; /* version number of this xscreensaver build */
+
+ GtkWidget *toplevel_widget; /* the main window */
+ GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
+ GtkWidget *popup_widget; /* the "Settings" dialog */
+ conf_data *cdata; /* private data for per-hack configuration */
+
+#ifdef HAVE_GTK2
+ GladeXML *glade_ui; /* Glade UI file */
+#endif /* HAVE_GTK2 */
+
+ Bool debug_p; /* whether to print diagnostics */
+ Bool initializing_p; /* flag for breaking recursion loops */
+ Bool saving_p; /* flag for breaking recursion loops */
+
+ char *desired_preview_cmd; /* subprocess we intend to run */
+ char *running_preview_cmd; /* subprocess we are currently running */
+ pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
+ Bool running_preview_error_p; /* whether the pid died abnormally */
+
+ Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
+ int subproc_timer_id; /* timer to delay subproc launch */
+ int subproc_check_timer_id; /* timer to check whether it started up */
+ int subproc_check_countdown; /* how many more checks left */
-char *blurb (void) { return progname; }
+ int *list_elt_to_hack_number; /* table for sorting the hack list */
+ int *hack_number_to_list_elt; /* the inverse table */
+ Bool *hacks_available_p; /* whether hacks are on $PATH */
+ int total_available; /* how many are on $PATH */
+ int list_count; /* how many items are in the list: this may be
+ less than p->screenhacks_count, if some are
+ suppressed. */
-static char *short_version = 0;
+ int _selected_list_element; /* don't use this: call
+ selected_list_element() instead */
+
+ int nscreens; /* How many X or Xinerama screens there are */
+
+ saver_preferences prefs;
+
+} state;
+
+
+/* Total fucking evilness due to the fact that it's rocket science to get
+ a closure object of our own down into the various widget callbacks. */
+static state *global_state_kludge;
Atom XA_VROOT;
Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
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);
+static void populate_demo_window (state *, int list_elt);
+static void populate_prefs_page (state *);
+static void populate_popup_window (state *);
+
+static Bool flush_dialog_changes_and_save (state *);
+static Bool flush_popup_changes_and_save (state *);
+
+static int maybe_reload_init_file (state *);
+static void await_xscreensaver (state *);
+static Bool xscreensaver_running_p (state *);
+static void sensitize_menu_items (state *s, Bool force_p);
+static void force_dialog_repaint (state *s);
+
+static void schedule_preview (state *, const char *cmd);
+static void kill_preview_subproc (state *, Bool reset_p);
+static void schedule_preview_check (state *);
+
+\f
+/* Prototypes of functions used by the Glade-generated code,
+ to avoid warnings.
+ */
+void exit_menu_cb (GtkMenuItem *, gpointer user_data);
+void about_menu_cb (GtkMenuItem *, gpointer user_data);
+void doc_menu_cb (GtkMenuItem *, gpointer user_data);
+void file_menu_cb (GtkMenuItem *, gpointer user_data);
+void activate_menu_cb (GtkMenuItem *, gpointer user_data);
+void lock_menu_cb (GtkMenuItem *, gpointer user_data);
+void kill_menu_cb (GtkMenuItem *, gpointer user_data);
+void restart_menu_cb (GtkWidget *, gpointer user_data);
+void run_this_cb (GtkButton *, gpointer user_data);
+void manual_cb (GtkButton *, gpointer user_data);
+void run_next_cb (GtkButton *, gpointer user_data);
+void run_prev_cb (GtkButton *, gpointer user_data);
+void pref_changed_cb (GtkWidget *, gpointer user_data);
+gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
+void mode_menu_item_cb (GtkWidget *, gpointer user_data);
+void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
+ gint page_num, gpointer user_data);
+void browse_image_dir_cb (GtkButton *, gpointer user_data);
+void browse_text_file_cb (GtkButton *, gpointer user_data);
+void browse_text_program_cb (GtkButton *, gpointer user_data);
+void settings_cb (GtkButton *, gpointer user_data);
+void settings_adv_cb (GtkButton *, gpointer user_data);
+void settings_std_cb (GtkButton *, gpointer user_data);
+void settings_reset_cb (GtkButton *, gpointer user_data);
+void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
+ gint page_num, gpointer user_data);
+void settings_cancel_cb (GtkButton *, gpointer user_data);
+void settings_ok_cb (GtkButton *, gpointer user_data);
+
+static void kill_gnome_screensaver (void);
+static void kill_kde_screensaver (void);
\f
/* Some random utility functions
*/
+const char *blurb (void);
+
+const char *
+blurb (void)
+{
+ time_t now = time ((time_t *) 0);
+ char *ct = (char *) ctime (&now);
+ static char buf[255];
+ int n = strlen(progname);
+ if (n > 100) n = 99;
+ strncpy(buf, progname, n);
+ buf[n++] = ':';
+ buf[n++] = ' ';
+ strncpy(buf+n, ct+11, 8);
+ strcpy(buf+n+9, ": ");
+ return buf;
+}
+
+
static GtkWidget *
-name_to_widget (GtkWidget *widget, const char *name)
+name_to_widget (state *s, const char *name)
{
- return (GtkWidget *) gtk_object_get_data (GTK_OBJECT(toplevel_widget), name);
+ GtkWidget *w;
+ if (!s) abort();
+ if (!name) abort();
+ if (!*name) abort();
+
+#ifdef HAVE_GTK2
+ if (!s->glade_ui)
+ {
+ /* First try to load the Glade file from the current directory;
+ if there isn't one there, check the installed directory.
+ */
+# define GLADE_FILE_NAME "xscreensaver-demo.glade2"
+ const char * const files[] = { GLADE_FILE_NAME,
+ GLADE_DIR "/" GLADE_FILE_NAME };
+ int i;
+ for (i = 0; i < countof (files); i++)
+ {
+ struct stat st;
+ if (!stat (files[i], &st))
+ {
+ s->glade_ui = glade_xml_new (files[i], NULL, NULL);
+ break;
+ }
+ }
+ if (!s->glade_ui)
+ {
+ fprintf (stderr,
+ "%s: could not load \"" GLADE_FILE_NAME "\"\n"
+ "\tfrom " GLADE_DIR "/ or current directory.\n",
+ blurb());
+ exit (-1);
+ }
+# undef GLADE_FILE_NAME
+
+ glade_xml_signal_autoconnect (s->glade_ui);
+ }
+
+ w = glade_xml_get_widget (s->glade_ui, name);
+
+#else /* !HAVE_GTK2 */
+
+ w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
+ name);
+ if (w) return w;
+ w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
+ name);
+#endif /* HAVE_GTK2 */
+ if (w) return w;
+
+ fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
+ blurb(), name);
+ abort();
}
static void
ensure_selected_item_visible (GtkWidget *widget)
{
+#ifdef HAVE_GTK2
+ GtkTreePath *path;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ path = gtk_tree_path_new_first ();
+ else
+ path = gtk_tree_model_get_path (model, &iter);
+
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
+
+ gtk_tree_path_free (path);
+
+#else /* !HAVE_GTK2 */
+
GtkScrolledWindow *scroller = 0;
GtkViewport *vp = 0;
GtkList *list_widget = 0;
GList *kids;
int nkids = 0;
GtkWidget *selected = 0;
- int which = -1;
+ int list_elt = -1;
GtkAdjustment *adj;
gint parent_h, child_y, child_h, children_h, ignore;
double ratio_t, ratio_b;
if (!selected)
return;
- which = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
+ list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
kids; kids = kids->next)
adj = gtk_scrolled_window_get_vadjustment (scroller);
- gdk_window_get_geometry (GTK_WIDGET(vp)->window,
+ gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
&ignore, &ignore, &ignore, &parent_h, &ignore);
- gdk_window_get_geometry (GTK_WIDGET(selected)->window,
+ gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
&ignore, &child_y, &ignore, &child_h, &ignore);
children_h = nkids * child_h;
gtk_adjustment_set_value (adj, target);
}
+#endif /* !HAVE_GTK2 */
}
static void
warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
{
GtkWidget *shell = GTK_WIDGET (user_data);
- while (shell->parent)
- shell = shell->parent;
+ while (GET_PARENT (shell))
+ shell = GET_PARENT (shell);
gtk_widget_destroy (GTK_WIDGET (shell));
}
warning_dialog_dismiss_cb (widget, user_data);
}
-static void
+static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
+{
+ kill_gnome_screensaver ();
+ warning_dialog_dismiss_cb (widget, user_data);
+}
+
+static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
+{
+ kill_kde_screensaver ();
+ warning_dialog_dismiss_cb (widget, user_data);
+}
+
+typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
+
+static Bool
warning_dialog (GtkWidget *parent, const char *message,
- Boolean restart_button_p, int center)
+ dialog_button button_type, int center)
{
char *msg = strdup (message);
char *head;
GtkWidget *cancel = 0;
int i = 0;
- while (parent->parent)
- parent = parent->parent;
+ while (parent && !GET_WINDOW (parent))
+ parent = GET_PARENT (parent);
- if (!GTK_WIDGET (parent)->window) /* too early to pop up transient dialogs */
- return;
+ if (!parent ||
+ !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
+ {
+ fprintf (stderr, "%s: too early for dialog?\n", progname);
+ free(msg);
+ return False;
+ }
head = msg;
while (head)
{
label = gtk_label_new (head);
+#ifdef HAVE_GTK2
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+#endif /* HAVE_GTK2 */
+#ifndef HAVE_GTK2
if (i == 1)
{
GTK_WIDGET (label)->style =
gtk_widget_set_style (GTK_WIDGET (label),
GTK_WIDGET (label)->style);
}
-
+#endif /* !HAVE_GTK2 */
if (center <= 0)
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+ gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
label, TRUE, TRUE, 0);
gtk_widget_show (label);
}
}
label = gtk_label_new ("");
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+ gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
label, TRUE, TRUE, 0);
gtk_widget_show (label);
label = gtk_hbutton_box_new ();
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
+ gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
label, TRUE, TRUE, 0);
+#ifdef HAVE_GTK2
+ if (button_type != D_NONE)
+ {
+ cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_container_add (GTK_CONTAINER (label), cancel);
+ }
+
+ ok = gtk_button_new_from_stock (GTK_STOCK_OK);
+ gtk_container_add (GTK_CONTAINER (label), ok);
+
+#else /* !HAVE_GTK2 */
+
ok = gtk_button_new_with_label ("OK");
gtk_container_add (GTK_CONTAINER (label), ok);
- if (restart_button_p)
+ if (button_type != D_NONE)
{
cancel = gtk_button_new_with_label ("Cancel");
gtk_container_add (GTK_CONTAINER (label), cancel);
}
+#endif /* !HAVE_GTK2 */
+
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);
+ SET_CAN_DEFAULT (ok);
gtk_widget_show (ok);
+ gtk_widget_grab_focus (ok);
+
if (cancel)
- gtk_widget_show (cancel);
+ {
+ SET_CAN_DEFAULT (cancel);
+ gtk_widget_show (cancel);
+ }
gtk_widget_show (label);
gtk_widget_show (dialog);
-/* gtk_window_set_default (GTK_WINDOW (dialog), ok);*/
- if (restart_button_p)
+ if (button_type != D_NONE)
{
- gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
- GTK_SIGNAL_FUNC (warning_dialog_restart_cb),
+ GtkSignalFunc fn;
+ switch (button_type) {
+ case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
+ case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
+ case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
+ default: abort(); break;
+ }
+ gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
(gpointer) dialog);
gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
(gpointer) dialog);
}
- gdk_window_set_transient_for (GTK_WIDGET (dialog)->window,
- GTK_WIDGET (parent)->window);
+ gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
+ GET_WINDOW (GTK_WIDGET (parent)));
+#ifdef HAVE_GTK2
+ gtk_window_present (GTK_WINDOW (dialog));
+#else /* !HAVE_GTK2 */
gdk_window_show (GTK_WIDGET (dialog)->window);
gdk_window_raise (GTK_WIDGET (dialog)->window);
+#endif /* !HAVE_GTK2 */
free (msg);
+ return True;
}
static void
-run_cmd (GtkWidget *widget, Atom command, int arg)
+run_cmd (state *s, Atom command, int arg)
{
char *err = 0;
int status;
- apply_changes_and_save (widget);
+ flush_dialog_changes_and_save (s);
status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
+
+ /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
+ if (status < 0 && err && strstr (err, "unexpectedly deleted"))
+ status = 0;
+
if (status < 0)
{
char buf [255];
sprintf (buf, "Error:\n\n%s", err);
else
strcpy (buf, "Unknown error!");
- warning_dialog (widget, buf, False, 100);
+ warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
}
if (err) free (err);
+
+ sensitize_menu_items (s, True);
+ force_dialog_repaint (s);
}
static void
-run_hack (GtkWidget *widget, int which, Bool report_errors_p)
+run_hack (state *s, int list_elt, 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
+ int hack_number;
+ char *err = 0;
+ int status;
+
+ if (list_elt < 0) return;
+ hack_number = s->list_elt_to_hack_number[list_elt];
+
+ flush_dialog_changes_and_save (s);
+ schedule_preview (s, 0);
+
+ status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
+ False, &err);
+
+ if (status < 0 && report_errors_p)
{
- char *s = 0;
- xscreensaver_command (GDK_DISPLAY(), XA_DEMO, which + 1, False, &s);
- if (s) free (s);
+ if (xscreensaver_running_p (s))
+ {
+ /* Kludge: ignore the spurious "window unexpectedly deleted"
+ errors... */
+ if (err && strstr (err, "unexpectedly deleted"))
+ status = 0;
+
+ if (status < 0)
+ {
+ char buf [255];
+ if (err)
+ sprintf (buf, "Error:\n\n%s", err);
+ else
+ strcpy (buf, "Unknown error!");
+ warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
+ }
+ }
+ else
+ {
+ /* The error is that the daemon isn't running;
+ offer to restart it.
+ */
+ const char *d = DisplayString (GDK_DISPLAY());
+ char msg [1024];
+ sprintf (msg,
+ _("Warning:\n\n"
+ "The XScreenSaver daemon doesn't seem to be running\n"
+ "on display \"%s\". Launch it now?"),
+ d);
+ warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
+ }
}
+
+ if (err) free (err);
+
+ sensitize_menu_items (s, False);
}
\f
/* Button callbacks
+
+ According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
+ libglade work on Cygwin; apparently all Glade callbacks need this magic
+ extra declaration. I do not pretend to understand.
*/
-void
+G_MODULE_EXPORT void
exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
{
- apply_changes_and_save (GTK_WIDGET (menuitem));
+ state *s = global_state_kludge; /* I hate C so much... */
+ flush_dialog_changes_and_save (s);
+ kill_preview_subproc (s, False);
gtk_main_quit ();
}
-static void
-wm_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
+static gboolean
+wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
{
- apply_changes_and_save (widget);
+ state *s = (state *) data;
+ flush_dialog_changes_and_save (s);
gtk_main_quit ();
+ return TRUE;
}
-void
-cut_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
-{
- /* #### */
- warning_dialog (GTK_WIDGET (menuitem),
- "Error:\n\n"
- "cut unimplemented\n", False, 1);
-}
-
-
-void
-copy_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
-{
- /* #### */
- warning_dialog (GTK_WIDGET (menuitem),
- "Error:\n\n"
- "copy unimplemented\n", False, 1);
-}
-
-
-void
-paste_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
-{
- /* #### */
- warning_dialog (GTK_WIDGET (menuitem),
- "Error:\n\n"
- "paste unimplemented\n", False, 1);
-}
-
-
-void
+G_MODULE_EXPORT void
about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
{
char msg [2048];
char *vers = strdup (screensaver_id + 4);
- char *s;
+ char *s, *s2;
char copy[1024];
- char *desc = "For updates, check http://www.jwz.org/xscreensaver/";
+ char year[5];
+ char *desc = _("For updates, check http://www.jwz.org/xscreensaver/");
s = strchr (vers, ',');
*s = 0;
s += 2;
- sprintf(copy, "Copyright \251 1991-2001 %s", s);
+ s2 = vers;
+ s2 = strrchr (vers, '-');
+ s2++;
+ strncpy (year, s2, 4);
+ year[4] = 0;
+
+ /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
+ non-ASCII characters aren't allowed in localizable string keys."
+ (I don't want to just use (c) instead of © because that doesn't
+ look as good in the plain-old default Latin1 "C" locale.)
+ */
+#ifdef HAVE_GTK2
+ sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
+#else /* !HAVE_GTK2 */
+ sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
+#endif /* !HAVE_GTK2 */
sprintf (msg, "%s\n\n%s", copy, desc);
GtkWidget *dialog = gtk_dialog_new ();
GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
GtkWidget *parent = GTK_WIDGET (menuitem);
- while (parent->parent)
- parent = parent->parent;
+ while (GET_PARENT (parent))
+ parent = GET_PARENT (parent);
hbox = gtk_hbox_new (FALSE, 20);
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+ gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
hbox, TRUE, TRUE, 0);
colormap = gtk_widget_get_colormap (parent);
gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
+#ifndef HAVE_GTK2
GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
GTK_WIDGET (label1)->style->font =
gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
+#endif /* HAVE_GTK2 */
label2 = gtk_label_new (msg);
gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
+#ifndef HAVE_GTK2
GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
GTK_WIDGET (label2)->style->font =
gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
+#endif /* HAVE_GTK2 */
hb = gtk_hbutton_box_new ();
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
+ gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
hb, TRUE, TRUE, 0);
- ok = gtk_button_new_with_label ("OK");
+#ifdef HAVE_GTK2
+ ok = gtk_button_new_from_stock (GTK_STOCK_OK);
+#else /* !HAVE_GTK2 */
+ ok = gtk_button_new_with_label (_("OK"));
+#endif /* !HAVE_GTK2 */
gtk_container_add (GTK_CONTAINER (hb), ok);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
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);
- gdk_window_show (GTK_WIDGET (dialog)->window);
- gdk_window_raise (GTK_WIDGET (dialog)->window);
+ gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
+ GET_WINDOW (GTK_WIDGET (parent)));
+ gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
+ gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
}
}
-void
+G_MODULE_EXPORT void
doc_menu_cb (GtkMenuItem *menuitem, 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;
+ state *s = global_state_kludge; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
char *help_command;
if (!p->help_url || !*p->help_url)
{
- warning_dialog (GTK_WIDGET (menuitem),
- "Error:\n\n"
- "No Help URL has been specified.\n", False, 100);
+ warning_dialog (s->toplevel_widget,
+ _("Error:\n\n"
+ "No Help URL has been specified.\n"), D_NONE, 100);
return;
}
help_command = (char *) malloc (strlen (p->load_url_command) +
- (strlen (p->help_url) * 2) + 20);
+ (strlen (p->help_url) * 4) + 20);
strcpy (help_command, "( ");
sprintf (help_command + strlen(help_command),
- p->load_url_command, p->help_url, p->help_url);
+ p->load_url_command,
+ p->help_url, p->help_url, p->help_url, p->help_url);
strcat (help_command, " ) &");
- system (help_command);
+ if (system (help_command) < 0)
+ fprintf (stderr, "%s: fork error\n", blurb());
free (help_command);
}
-void
+G_MODULE_EXPORT void
+file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ sensitize_menu_items (s, False);
+}
+
+
+G_MODULE_EXPORT void
activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
{
- run_cmd (GTK_WIDGET (menuitem), XA_ACTIVATE, 0);
+ state *s = global_state_kludge; /* I hate C so much... */
+ run_cmd (s, XA_ACTIVATE, 0);
}
-void
+G_MODULE_EXPORT void
lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
{
- run_cmd (GTK_WIDGET (menuitem), XA_LOCK, 0);
+ state *s = global_state_kludge; /* I hate C so much... */
+ run_cmd (s, XA_LOCK, 0);
}
-void
+G_MODULE_EXPORT void
kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
{
- run_cmd (GTK_WIDGET (menuitem), XA_EXIT, 0);
+ state *s = global_state_kludge; /* I hate C so much... */
+ run_cmd (s, XA_EXIT, 0);
}
-void
+G_MODULE_EXPORT void
restart_menu_cb (GtkWidget *widget, gpointer user_data)
{
-#if 0
- run_cmd (GTK_WIDGET (widget), XA_RESTART, 0);
-#else
- apply_changes_and_save (GTK_WIDGET (widget));
+ state *s = global_state_kludge; /* I hate C so much... */
+ flush_dialog_changes_and_save (s);
xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
sleep (1);
- system ("xscreensaver -nosplash &");
-#endif
+ if (system ("xscreensaver -nosplash &") < 0)
+ fprintf (stderr, "%s: fork error\n", blurb());
- await_xscreensaver (GTK_WIDGET (widget));
+ await_xscreensaver (s);
}
-static void
-await_xscreensaver (GtkWidget *widget)
+static Bool
+xscreensaver_running_p (state *s)
{
- int countdown = 5;
-
Display *dpy = GDK_DISPLAY();
- /* GtkWidget *dialog = 0;*/
char *rversion = 0;
+ server_xscreensaver_version (dpy, &rversion, 0, 0);
+ if (!rversion)
+ return False;
+ free (rversion);
+ return True;
+}
- while (!rversion && (--countdown > 0))
- {
- /* Check for the version of the running xscreensaver... */
- server_xscreensaver_version (dpy, &rversion, 0, 0);
+static void
+await_xscreensaver (state *s)
+{
+ int countdown = 5;
+ Bool ok = False;
- /* If it's not there yet, wait a second... */
- sleep (1);
- }
+ while (!ok && (--countdown > 0))
+ if (xscreensaver_running_p (s))
+ ok = True;
+ else
+ sleep (1); /* If it's not there yet, wait a second... */
-/* if (dialog) gtk_widget_destroy (dialog);*/
+ sensitize_menu_items (s, True);
- if (rversion)
- {
- /* Got it. */
- free (rversion);
- }
- else
+ if (! ok)
{
/* Timed out, no screensaver running. */
Bool root_p = (geteuid () == 0);
strcpy (buf,
- "Error:\n\n"
- "The xscreensaver daemon did not start up properly.\n"
- "\n");
+ _("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"
+
+# ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than
+ the length ISO C89 compilers are required to
+ support" in the following expression... */
+# endif
+ strcat (buf, STFU
+ _("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"
"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.");
+ "normal user, and `su' as necessary."));
else
- strcat (buf, "Please check your $PATH and permissions.");
+ strcat (buf, _("Please check your $PATH and permissions."));
- warning_dialog (widget, buf, False, 1);
+ warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
}
-}
+ force_dialog_repaint (s);
+}
-static int _selected_hack_number = -1;
static int
-selected_hack_number (GtkWidget *toplevel)
+selected_list_element (state *s)
{
-#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;
- GtkWidget *selected = (slist ? GTK_WIDGET (slist->data) : 0);
- int which = (selected
- ? gtk_list_child_position (list_widget, GTK_WIDGET (selected))
- : -1);
- return which;
-#else
- return _selected_hack_number;
-#endif
+ return s->_selected_list_element;
}
static int
-demo_write_init_file (GtkWidget *widget, saver_preferences *p)
+demo_write_init_file (state *s, saver_preferences *p)
{
- if (!write_init_file (p, short_version, False))
- return 0;
+ Display *dpy = GDK_DISPLAY();
+
+#if 0
+ /* #### try to figure out why shit keeps getting reordered... */
+ if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
+ abort();
+#endif
+
+ if (!write_init_file (dpy, p, s->short_version, False))
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
+ 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);
+ warning_dialog (s->toplevel_widget,
+ _("Error:\n\nCouldn't determine init file name!\n"),
+ D_NONE, 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);
+ sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
+ warning_dialog (s->toplevel_widget, b, D_NONE, 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 (widget, "list"));
- int which = selected_hack_number (widget);
-
- GtkEntry *cmd = GTK_ENTRY (name_to_widget (widget, "cmd_text"));
- GtkToggleButton *enabled =
- 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));
- const char *command = gtk_entry_get_text (cmd);
-
- char c;
- unsigned long id;
-
- 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, "")) 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";
- else if (!strcasecmp (visual, "mono")) visual = "Mono";
- else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
- else if (!strcasecmp (visual, "gray")) visual = "Gray";
- else if (!strcasecmp (visual, "grey")) visual = "Gray";
- else if (!strcasecmp (visual, "color")) visual = "Color";
- else if (!strcasecmp (visual, "gl")) visual = "GL";
- else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
- else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
- else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
- else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
- else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
- else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
- else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
- else if (1 == sscanf (visual, " %ld %c", &id, &c)) ;
- else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
- else
- {
- gdk_beep (); /* unparsable */
- visual = "";
- gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), "Any");
- }
-
- ensure_selected_item_visible (GTK_WIDGET (list_widget));
-
- 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);
-}
-
-
-void
+G_MODULE_EXPORT void
run_this_cb (GtkButton *button, gpointer user_data)
{
- int which = selected_hack_number (GTK_WIDGET (button));
- if (which < 0) return;
- if (0 == apply_changes_and_save (GTK_WIDGET (button)))
- run_hack (GTK_WIDGET (button), which, True);
+ state *s = global_state_kludge; /* I hate C so much... */
+ int list_elt = selected_list_element (s);
+ if (list_elt < 0) return;
+ if (!flush_dialog_changes_and_save (s))
+ run_hack (s, list_elt, True);
}
-void
+G_MODULE_EXPORT void
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));
-
- name = strdup (p->screenhacks[which]->command);
+ Display *dpy = GDK_DISPLAY();
+ state *s = global_state_kludge; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
+ GtkWidget *list_widget = name_to_widget (s, "list");
+ int list_elt = selected_list_element (s);
+ int hack_number;
+ char *name, *name2, *cmd, *str;
+ char *oname = 0;
+ if (list_elt < 0) return;
+ hack_number = s->list_elt_to_hack_number[list_elt];
+
+ flush_dialog_changes_and_save (s);
+ ensure_selected_item_visible (list_widget);
+
+ name = strdup (p->screenhacks[hack_number]->command);
name2 = name;
+ oname = name;
while (isspace (*name2)) name2++;
- s = name2;
- while (*s && !isspace (*s)) s++;
- *s = 0;
- s = strrchr (name2, '/');
- if (s) name = s+1;
+ str = name2;
+ while (*str && !isspace (*str)) str++;
+ *str = 0;
+ str = strrchr (name2, '/');
+ if (str) name2 = str+1;
- cmd = get_string_resource ("manualCommand", "ManualCommand");
+ cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
if (cmd)
{
- char *cmd2 = (char *) malloc (strlen (cmd) + strlen (name2) + 100);
+ char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
strcpy (cmd2, "( ");
sprintf (cmd2 + strlen (cmd2),
cmd,
name2, name2, name2, name2);
strcat (cmd2, " ) &");
- system (cmd2);
+ if (system (cmd2) < 0)
+ fprintf (stderr, "%s: fork error\n", blurb());
free (cmd2);
}
else
{
warning_dialog (GTK_WIDGET (button),
- "Error:\n\nno `manualCommand' resource set.",
- False, 100);
+ _("Error:\n\nno `manualCommand' resource set."),
+ D_NONE, 100);
}
- free (name);
+ free (oname);
}
-void
+static void
+force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
+{
+ GtkWidget *parent = name_to_widget (s, "scroller");
+ gboolean was = GET_SENSITIVE (parent);
+#ifdef HAVE_GTK2
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+#endif /* HAVE_GTK2 */
+
+ if (!was) gtk_widget_set_sensitive (parent, True);
+#ifdef HAVE_GTK2
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+ g_assert (model);
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
+ {
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+#else /* !HAVE_GTK2 */
+ gtk_list_select_item (GTK_LIST (list), list_elt);
+#endif /* !HAVE_GTK2 */
+ if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
+ if (!was) gtk_widget_set_sensitive (parent, False);
+}
+
+
+G_MODULE_EXPORT void
run_next_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;
+ state *s = global_state_kludge; /* I hate C so much... */
+ /* saver_preferences *p = &s->prefs; */
+ Bool ops = s->preview_suppressed_p;
- GtkList *list_widget =
- GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
- int which = selected_hack_number (GTK_WIDGET (button));
+ GtkWidget *list_widget = name_to_widget (s, "list");
+ int list_elt = selected_list_element (s);
- if (which < 0)
- which = 0;
+ if (list_elt < 0)
+ list_elt = 0;
else
- which++;
+ list_elt++;
+
+ if (list_elt >= s->list_count)
+ list_elt = 0;
- if (which >= p->screenhacks_count)
- which = 0;
+ s->preview_suppressed_p = True;
- 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 (GTK_WIDGET (button), which, False);
+ flush_dialog_changes_and_save (s);
+ force_list_select_item (s, list_widget, list_elt, True);
+ populate_demo_window (s, list_elt);
+ run_hack (s, list_elt, False);
+
+ s->preview_suppressed_p = ops;
}
-void
+G_MODULE_EXPORT void
run_prev_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;
+ state *s = global_state_kludge; /* I hate C so much... */
+ /* saver_preferences *p = &s->prefs; */
+ Bool ops = s->preview_suppressed_p;
- GtkList *list_widget =
- GTK_LIST (name_to_widget (GTK_WIDGET (button), "list"));
- int which = selected_hack_number (GTK_WIDGET (button));
+ GtkWidget *list_widget = name_to_widget (s, "list");
+ int list_elt = selected_list_element (s);
- if (which < 0)
- which = p->screenhacks_count - 1;
+ if (list_elt < 0)
+ list_elt = s->list_count - 1;
else
- which--;
+ list_elt--;
+
+ if (list_elt < 0)
+ list_elt = s->list_count - 1;
- if (which < 0)
- which = p->screenhacks_count - 1;
+ s->preview_suppressed_p = True;
- 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 (GTK_WIDGET (button), which, False);
+ flush_dialog_changes_and_save (s);
+ force_list_select_item (s, list_widget, list_elt, True);
+ populate_demo_window (s, list_elt);
+ run_hack (s, list_elt, False);
+
+ s->preview_suppressed_p = ops;
}
-/* Helper for the text fields that contain time specifications:
- this parses the text, and does error checking.
+/* Writes the given settings into prefs.
+ Returns true if there was a change, False otherwise.
+ command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
*/
-static void
-hack_time_text (GtkWidget *widget, const char *line, Time *store, Bool sec_p)
+static Bool
+flush_changes (state *s,
+ int list_elt,
+ int enabled_p,
+ const char *command,
+ const char *visual)
{
- if (*line)
+ saver_preferences *p = &s->prefs;
+ Bool changed = False;
+ screenhack *hack;
+ int hack_number;
+ if (list_elt < 0 || list_elt >= s->list_count)
+ abort();
+
+ hack_number = s->list_elt_to_hack_number[list_elt];
+ hack = p->screenhacks[hack_number];
+
+ if (enabled_p != -1 &&
+ enabled_p != hack->enabled_p)
+ {
+ hack->enabled_p = enabled_p;
+ changed = True;
+ if (s->debug_p)
+ fprintf (stderr, "%s: \"%s\": enabled => %d\n",
+ blurb(), hack->name, enabled_p);
+ }
+
+ if (command)
+ {
+ if (!hack->command || !!strcmp (command, hack->command))
+ {
+ if (hack->command) free (hack->command);
+ hack->command = strdup (command);
+ changed = True;
+ if (s->debug_p)
+ fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
+ blurb(), hack->name, command);
+ }
+ }
+
+ if (visual)
+ {
+ const char *ov = hack->visual;
+ if (!ov || !*ov) ov = "any";
+ if (!*visual) visual = "any";
+ if (!!strcasecmp (visual, ov))
+ {
+ if (hack->visual) free (hack->visual);
+ hack->visual = strdup (visual);
+ changed = True;
+ if (s->debug_p)
+ fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
+ blurb(), hack->name, visual);
+ }
+ }
+
+ return changed;
+}
+
+
+/* Helper for the text fields that contain time specifications:
+ this parses the text, and does error checking.
+ */
+static void
+hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
+{
+ if (*line)
{
int value;
- value = parse_time ((char *) line, sec_p, True);
+ if (!sec_p || strchr (line, ':'))
+ value = parse_time ((char *) line, sec_p, True);
+ else
+ {
+ char c;
+ if (sscanf (line, "%d%c", &value, &c) != 1)
+ value = -1;
+ if (!sec_p)
+ value *= 60;
+ }
+
value *= 1000; /* Time measures in microseconds */
if (value < 0)
{
char b[255];
sprintf (b,
- "Error:\n\n"
- "Unparsable time format: \"%s\"\n",
+ _("Error:\n\n"
+ "Unparsable time format: \"%s\"\n"),
line);
- warning_dialog (widget, b, False, 100);
+ warning_dialog (s->toplevel_widget, b, D_NONE, 100);
}
else
*store = value;
return True;
}
+static Bool
+file_p (const char *path)
+{
+ struct stat st;
+ if (!path || !*path)
+ return False;
+ else if (stat (path, &st))
+ return False;
+ else if (S_ISDIR (st.st_mode))
+ return False;
+ else
+ return True;
+}
+
static char *
normalize_directory (const char *path)
{
int L;
char *p2, *s;
- if (!path) return 0;
- L = strlen (path);;
+ if (!path || !*path) return 0;
+ L = strlen (path);
p2 = (char *) malloc (L + 2);
strcpy (p2, path);
if (p2[L-1] == '/') /* remove trailing slash */
{
s0--;
s += 3;
- strcpy (s0, s);
+ /* strcpy (s0, s); */
+ memmove(s0, s, strlen(s) + 1);
s = s0-1;
}
}
- else if (*s == '/' && !strncmp (s, "/./", 3)) /* delete "/./" */
- strcpy (s, s+2), s--;
+ else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
+ /* strcpy (s, s+2), s--; */
+ memmove(s, s+2, strlen(s+2) + 1);
+ s--;
+ }
else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
*s = 0, s--;
}
- for (s = p2; s && *s; s++) /* normalize consecutive slashes */
- while (s[0] == '/' && s[1] == '/')
- strcpy (s, s+1);
+ /*
+ Normalize consecutive slashes.
+ Ignore doubled slashes after ":" to avoid mangling URLs.
+ */
+
+ for (s = p2; s && *s; s++){
+ if (*s == ':') continue;
+ if (!s[1] || !s[2]) continue;
+ while (s[1] == '/' && s[2] == '/')
+ /* strcpy (s+1, s+2); */
+ memmove (s+1, s+2, strlen(s+2) + 1);
+ }
+
+ /* and strip trailing whitespace for good measure. */
+ L = strlen(p2);
+ while (isspace(p2[L-1]))
+ p2[--L] = 0;
return p2;
}
-void
-prefs_ok_cb (GtkButton *button, gpointer user_data)
+#ifdef HAVE_GTK2
+
+typedef struct {
+ state *s;
+ int i;
+ Bool *changed;
+} FlushForeachClosure;
+
+static gboolean
+flush_checkbox (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ FlushForeachClosure *closure = data;
+ gboolean checked;
+
+ gtk_tree_model_get (model, iter,
+ COL_ENABLED, &checked,
+ -1);
+
+ if (flush_changes (closure->s, closure->i,
+ checked, 0, 0))
+ *closure->changed = True;
+
+ closure->i++;
+
+ /* don't remove row */
+ return FALSE;
+}
+
+#endif /* HAVE_GTK2 */
+
+/* Flush out any changes made in the main dialog window (where changes
+ take place immediately: clicking on a checkbox causes the init file
+ to be written right away.)
+ */
+static Bool
+flush_dialog_changes_and_save (state *s)
{
- /* prefs_pair *pair = (prefs_pair *) client_data; */
- prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
+ saver_preferences P2, *p2 = &P2;
+#ifdef HAVE_GTK2
+ GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
+ FlushForeachClosure closure;
+#else /* !HAVE_GTK2 */
+ GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
+ GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
+ int i;
+#endif /* !HAVE_GTK2 */
+ static Bool already_warned_about_missing_image_directory = False; /* very long name... */
- saver_preferences *p = pair->a;
- saver_preferences *p2 = pair->b;
Bool changed = False;
+ GtkWidget *w;
-# define SECONDS(field, name) \
- 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_WIDGET(button), gtk_entry_get_text (\
- GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))), \
- (field), \
- False)
-
-# define INTEGER(field, name) do { \
- char *line = gtk_entry_get_text (\
- GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
- unsigned int value; \
- char c; \
- if (! *line) \
- ; \
- else if (sscanf (line, "%u%c", &value, &c) != 1) \
- { \
- 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 PATHNAME(field, name) do { \
- char *line = gtk_entry_get_text (\
- GTK_ENTRY (name_to_widget (GTK_WIDGET(button), (name)))); \
- if (! *line) \
- ; \
- else if (!directory_p (line)) \
- { \
- char b[255]; \
- sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", line); \
- warning_dialog (GTK_WIDGET (button), b, False, 100); \
- if ((field)) free ((field)); \
- (field) = strdup(line); \
- } \
- else { \
- if ((field)) free ((field)); \
- (field) = strdup(line); \
- } \
- } while(0)
+ if (s->saving_p) return False;
+ s->saving_p = True;
-# define CHECKBOX(field, name) \
- field = gtk_toggle_button_get_active (\
- GTK_TOGGLE_BUTTON (name_to_widget (GTK_WIDGET(button), (name))))
+ *p2 = *p;
+
+ /* Flush any checkbox changes in the list down into the prefs struct.
+ */
+#ifdef HAVE_GTK2
+ closure.s = s;
+ closure.changed = &changed;
+ closure.i = 0;
+ gtk_tree_model_foreach (model, flush_checkbox, &closure);
+
+#else /* !HAVE_GTK2 */
+
+ for (i = 0; kids; kids = kids->next, i++)
+ {
+ GtkWidget *line = GTK_WIDGET (kids->data);
+ GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
+ GtkWidget *line_check =
+ GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
+ Bool checked =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
+
+ if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
+ changed = True;
+ }
+#endif /* ~HAVE_GTK2 */
+
+ /* Flush the non-hack-specific settings down into the prefs struct.
+ */
+
+# define SECONDS(FIELD,NAME) \
+ w = name_to_widget (s, (NAME)); \
+ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
+
+# define MINUTES(FIELD,NAME) \
+ w = name_to_widget (s, (NAME)); \
+ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
+
+# define CHECKBOX(FIELD,NAME) \
+ w = name_to_widget (s, (NAME)); \
+ (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
- MINUTES (&p2->timeout, "timeout_text");
- MINUTES (&p2->cycle, "cycle_text");
+# define PATHNAME(FIELD,NAME) \
+ w = name_to_widget (s, (NAME)); \
+ (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
+
+# define TEXT(FIELD,NAME) \
+ w = name_to_widget (s, (NAME)); \
+ (FIELD) = (char *) gtk_entry_get_text (GTK_ENTRY (w))
+
+ MINUTES (&p2->timeout, "timeout_spinbutton");
+ MINUTES (&p2->cycle, "cycle_spinbutton");
CHECKBOX (p2->lock_p, "lock_button");
- MINUTES (&p2->lock_timeout, "lock_text");
+ MINUTES (&p2->lock_timeout, "lock_spinbutton");
CHECKBOX (p2->dpms_enabled_p, "dpms_button");
- MINUTES (&p2->dpms_standby, "dpms_standby_text");
- MINUTES (&p2->dpms_suspend, "dpms_suspend_text");
- MINUTES (&p2->dpms_off, "dpms_off_text");
+ CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
+ MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
+ MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
+ MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
CHECKBOX (p2->grab_video_p, "grab_video_button");
CHECKBOX (p2->random_image_p, "grab_image_button");
PATHNAME (p2->image_directory, "image_text");
+#if 0
CHECKBOX (p2->verbose_p, "verbose_button");
CHECKBOX (p2->capture_stderr_p, "capture_button");
CHECKBOX (p2->splash_p, "splash_button");
+#endif
+
+ {
+ Bool v = False;
+ CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
+ CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
+ CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
+ CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
+ CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
+ TEXT (p2->text_literal, "text_entry");
+ PATHNAME (p2->text_file, "text_file_entry");
+ PATHNAME (p2->text_program, "text_program_entry");
+ PATHNAME (p2->text_program, "text_program_entry");
+ TEXT (p2->text_url, "text_url_entry");
+ }
CHECKBOX (p2->install_cmap_p, "install_button");
CHECKBOX (p2->fade_p, "fade_button");
CHECKBOX (p2->unfade_p, "unfade_button");
- SECONDS (&p2->fade_seconds, "fade_text");
+ SECONDS (&p2->fade_seconds, "fade_spinbutton");
# undef SECONDS
# undef MINUTES
-# undef INTEGER
-# undef PATHNAME
# undef CHECKBOX
+# undef PATHNAME
+# undef TEXT
+
+ /* Warn if the image directory doesn't exist, when:
+ - not being warned before
+ - image directory is changed and the directory doesn't exist
+ - image directory does not begin with http://
+ */
+ if (p2->image_directory &&
+ *p2->image_directory &&
+ !directory_p (p2->image_directory) &&
+ strncmp(p2->image_directory, "http://", 6) &&
+ ( !already_warned_about_missing_image_directory ||
+ ( p->image_directory &&
+ *p->image_directory &&
+ strcmp(p->image_directory, p2->image_directory)
+ )
+ )
+ )
+ {
+ char b[255];
+ sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
+ p2->image_directory);
+ if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
+ already_warned_about_missing_image_directory = True;
+ }
+
+
+ /* Map the mode menu to `saver_mode' enum values. */
+ {
+ GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
+ GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
+ GtkWidget *selected = gtk_menu_get_active (menu);
+ GList *kids = gtk_container_children (GTK_CONTAINER (menu));
+ int menu_elt = g_list_index (kids, (gpointer) selected);
+ if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
+ p2->mode = mode_menu_order[menu_elt];
+ }
+
+ if (p2->mode == ONE_HACK)
+ {
+ int list_elt = selected_list_element (s);
+ p2->selected_hack = (list_elt >= 0
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ }
-# define COPY(field) \
- if (p->field != p2->field) changed = True; \
+# define COPY(field, name) \
+ if (p->field != p2->field) { \
+ changed = True; \
+ if (s->debug_p) \
+ fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
+ } \
p->field = p2->field
- COPY(timeout);
- COPY(cycle);
- COPY(lock_p);
- COPY(lock_timeout);
-
- COPY(dpms_enabled_p);
- COPY(dpms_standby);
- COPY(dpms_suspend);
- COPY(dpms_off);
-
- COPY (grab_desktop_p);
- COPY (grab_video_p);
- COPY (random_image_p);
-
- if (!p->image_directory ||
- !p2->image_directory ||
- strcmp(p->image_directory, p2->image_directory))
- changed = True;
- if (p->image_directory && p->image_directory != p2->image_directory)
- free (p->image_directory);
- p->image_directory = normalize_directory (p2->image_directory);
- if (p2->image_directory) free (p2->image_directory);
- p2->image_directory = 0;
-
- COPY(verbose_p);
- COPY(capture_stderr_p);
- COPY(splash_p);
-
- COPY(install_cmap_p);
- COPY(fade_p);
- COPY(unfade_p);
- COPY(fade_seconds);
+ COPY(mode, "mode");
+ COPY(selected_hack, "selected_hack");
+
+ COPY(timeout, "timeout");
+ COPY(cycle, "cycle");
+ COPY(lock_p, "lock_p");
+ COPY(lock_timeout, "lock_timeout");
+
+ COPY(dpms_enabled_p, "dpms_enabled_p");
+ COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
+ COPY(dpms_standby, "dpms_standby");
+ COPY(dpms_suspend, "dpms_suspend");
+ COPY(dpms_off, "dpms_off");
+
+#if 0
+ COPY(verbose_p, "verbose_p");
+ COPY(capture_stderr_p, "capture_stderr_p");
+ COPY(splash_p, "splash_p");
+#endif
+
+ COPY(tmode, "tmode");
+
+ COPY(install_cmap_p, "install_cmap_p");
+ COPY(fade_p, "fade_p");
+ COPY(unfade_p, "unfade_p");
+ COPY(fade_seconds, "fade_seconds");
+
+ COPY(grab_desktop_p, "grab_desktop_p");
+ COPY(grab_video_p, "grab_video_p");
+ COPY(random_image_p, "random_image_p");
+
# undef COPY
- populate_prefs_page (GTK_WIDGET (button), pair);
+# define COPYSTR(FIELD,NAME) \
+ if (!p->FIELD || \
+ !p2->FIELD || \
+ strcmp(p->FIELD, p2->FIELD)) \
+ { \
+ changed = True; \
+ if (s->debug_p) \
+ fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
+ } \
+ if (p->FIELD && p->FIELD != p2->FIELD) \
+ free (p->FIELD); \
+ p->FIELD = p2->FIELD; \
+ p2->FIELD = 0
+
+ COPYSTR(image_directory, "image_directory");
+ COPYSTR(text_literal, "text_literal");
+ COPYSTR(text_file, "text_file");
+ COPYSTR(text_program, "text_program");
+ COPYSTR(text_url, "text_url");
+# undef COPYSTR
+
+ populate_prefs_page (s);
if (changed)
{
Display *dpy = GDK_DISPLAY();
- sync_server_dpms_settings (dpy, p->dpms_enabled_p,
+ Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
+ sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
p->dpms_standby / 1000,
p->dpms_suspend / 1000,
p->dpms_off / 1000,
False);
- demo_write_init_file (GTK_WIDGET (button), p);
+ changed = demo_write_init_file (s, p);
+ }
+
+ s->saving_p = False;
+ return changed;
+}
+
+
+/* Flush out any changes made in the popup dialog box (where changes
+ take place only when the OK button is clicked.)
+ */
+static Bool
+flush_popup_changes_and_save (state *s)
+{
+ Bool changed = False;
+ saver_preferences *p = &s->prefs;
+ int list_elt = selected_list_element (s);
+
+ GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
+ GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
+
+ const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
+ const char *command = gtk_entry_get_text (cmd);
+
+ char c;
+ unsigned long id;
+
+ if (s->saving_p) return False;
+ s->saving_p = True;
+
+ if (list_elt < 0)
+ goto DONE;
+
+ if (maybe_reload_init_file (s) != 0)
+ {
+ changed = True;
+ goto DONE;
+ }
+
+ /* Sanity-check and canonicalize whatever the user typed into the combo box.
+ */
+ 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";
+ else if (!strcasecmp (visual, "mono")) visual = "Mono";
+ else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
+ else if (!strcasecmp (visual, "gray")) visual = "Gray";
+ else if (!strcasecmp (visual, "grey")) visual = "Gray";
+ else if (!strcasecmp (visual, "color")) visual = "Color";
+ else if (!strcasecmp (visual, "gl")) visual = "GL";
+ else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
+ else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
+ else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
+ else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
+ else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
+ else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
+ else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
+ else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
+ else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
+ else
+ {
+ gdk_beep (); /* unparsable */
+ visual = "";
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
+ }
+
+ changed = flush_changes (s, list_elt, -1, command, visual);
+ if (changed)
+ {
+ changed = demo_write_init_file (s, p);
+
+ /* Do this to re-launch the hack if (and only if) the command line
+ has changed. */
+ populate_demo_window (s, selected_list_element (s));
+ }
+
+ DONE:
+ s->saving_p = False;
+ return changed;
+}
+
+
+G_MODULE_EXPORT void
+pref_changed_cb (GtkWidget *widget, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ if (! s->initializing_p)
+ {
+ s->initializing_p = True;
+ flush_dialog_changes_and_save (s);
+ s->initializing_p = False;
+ }
+}
+
+G_MODULE_EXPORT gboolean
+pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ pref_changed_cb (widget, user_data);
+ return FALSE;
+}
+
+/* Callback on menu items in the "mode" options menu.
+ */
+G_MODULE_EXPORT void
+mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
+{
+ state *s = (state *) user_data;
+ saver_preferences *p = &s->prefs;
+ GtkWidget *list = name_to_widget (s, "list");
+ int list_elt;
+
+ GList *menu_items =
+ gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
+ int menu_index = 0;
+ saver_mode new_mode;
+
+ while (menu_items)
+ {
+ if (menu_items->data == widget)
+ break;
+ menu_index++;
+ menu_items = menu_items->next;
}
+ if (!menu_items) abort();
+
+ new_mode = mode_menu_order[menu_index];
+
+ /* Keep the same list element displayed as before; except if we're
+ switching *to* "one screensaver" mode from any other mode, set
+ "the one" to be that which is currently selected.
+ */
+ list_elt = selected_list_element (s);
+ if (new_mode == ONE_HACK)
+ p->selected_hack = s->list_elt_to_hack_number[list_elt];
+
+ {
+ saver_mode old_mode = p->mode;
+ p->mode = new_mode;
+ populate_demo_window (s, list_elt);
+ force_list_select_item (s, list, list_elt, True);
+ p->mode = old_mode; /* put it back, so the init file gets written */
+ }
+
+ pref_changed_cb (widget, user_data);
}
-void
-prefs_cancel_cb (GtkButton *button, gpointer user_data)
+G_MODULE_EXPORT void
+switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
+ gint page_num, gpointer user_data)
{
- /* prefs_pair *pair = (prefs_pair *) client_data; */
- prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
+ state *s = global_state_kludge; /* I hate C so much... */
+ pref_changed_cb (GTK_WIDGET (notebook), user_data);
- *pair->b = *pair->a;
- populate_prefs_page (GTK_WIDGET (button), pair);
+ /* If we're switching to page 0, schedule the current hack to be run.
+ Otherwise, schedule it to stop. */
+ if (page_num == 0)
+ populate_demo_window (s, selected_list_element (s));
+ else
+ schedule_preview (s, 0);
}
+#ifdef HAVE_GTK2
+static void
+list_activated_cb (GtkTreeView *list,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer data)
+{
+ state *s = data;
+ char *str;
+ int list_elt;
+
+ g_return_if_fail (!gdk_pointer_is_grabbed ());
-void
-pref_changed_cb (GtkButton *button, gpointer user_data)
+ str = gtk_tree_path_to_string (path);
+ list_elt = strtol (str, NULL, 10);
+ g_free (str);
+
+ if (list_elt >= 0)
+ run_hack (s, list_elt, True);
+}
+
+static void
+list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
{
- if (! initializing_p)
- apply_changes_and_save (GTK_WIDGET (button));
+ state *s = (state *)data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ char *str;
+ int list_elt;
+
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return;
+
+ path = gtk_tree_model_get_path (model, &iter);
+ str = gtk_tree_path_to_string (path);
+ list_elt = strtol (str, NULL, 10);
+
+ gtk_tree_path_free (path);
+ g_free (str);
+
+ populate_demo_window (s, list_elt);
+ flush_dialog_changes_and_save (s);
+
+ /* Re-populate the Settings window any time a new item is selected
+ in the list, in case both windows are currently visible.
+ */
+ populate_popup_window (s);
}
+#else /* !HAVE_GTK2 */
+
+static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
+ list_select_cb that comes in
+ *after* we've double-clicked.
+ */
static gint
list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
- gpointer client_data)
+ gpointer data)
{
+ state *s = (state *) data;
if (event->type == GDK_2BUTTON_PRESS)
{
- GtkList *list = GTK_LIST (name_to_widget (button, "list"));
- int which = gtk_list_child_position (list, GTK_WIDGET (button));
+ GtkList *list = GTK_LIST (name_to_widget (s, "list"));
+ int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
+
+ last_doubleclick_time = time ((time_t *) 0);
- if (which >= 0)
- run_hack (GTK_WIDGET (button), which, True);
+ if (list_elt >= 0)
+ run_hack (s, list_elt, True);
}
return FALSE;
static void
-list_select_cb (GtkList *list, GtkWidget *child)
+list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
{
- /* prefs_pair *pair = (prefs_pair *) client_data; */
- prefs_pair *pair = global_prefs_pair; /* I hate C so much... */
+ state *s = (state *) data;
+ time_t now = time ((time_t *) 0);
- 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);
+ if (now >= last_doubleclick_time + 2)
+ {
+ int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
+ populate_demo_window (s, list_elt);
+ flush_dialog_changes_and_save (s);
+ }
}
static void
-list_unselect_cb (GtkList *list, GtkWidget *child)
+list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
{
- /* 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);
+ state *s = (state *) data;
+ populate_demo_window (s, -1);
+ flush_dialog_changes_and_save (s);
}
+#endif /* !HAVE_GTK2 */
-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
also syncs this checkbox with the right pane Enabled checkbox.
*/
static void
-list_checkbox_cb (GtkWidget *cb, gpointer client_data)
+list_checkbox_cb (
+#ifdef HAVE_GTK2
+ GtkCellRendererToggle *toggle,
+ gchar *path_string,
+#else /* !HAVE_GTK2 */
+ GtkWidget *cb,
+#endif /* !HAVE_GTK2 */
+ gpointer data)
{
- prefs_pair *pair = (prefs_pair *) client_data;
-
+ state *s = (state *) data;
+
+#ifdef HAVE_GTK2
+ GtkScrolledWindow *scroller =
+ GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
+ GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkTreeModel *model = gtk_tree_view_get_model (list);
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ GtkTreeIter iter;
+ gboolean active;
+#else /* !HAVE_GTK2 */
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);
+#endif /* !HAVE_GTK2 */
GtkAdjustment *adj;
double scroll_top;
- GtkToggleButton *enabled =
- GTK_TOGGLE_BUTTON (name_to_widget (cb, "enabled"));
+ int list_elt;
+
+#ifdef HAVE_GTK2
+ if (!gtk_tree_model_get_iter (model, &iter, path))
+ {
+ g_warning ("bad path: %s", path_string);
+ return;
+ }
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (model, &iter,
+ COL_ENABLED, &active,
+ -1);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ COL_ENABLED, !active,
+ -1);
- int which = gtk_list_child_position (list, line);
+ list_elt = strtol (path_string, NULL, 10);
+#else /* !HAVE_GTK2 */
+ list_elt = gtk_list_child_position (list, line);
+#endif /* !HAVE_GTK2 */
/* remember previous scroll position of the top of the list */
adj = gtk_scrolled_window_get_vadjustment (scroller);
- scroll_top = adj->value;
+ scroll_top = GET_ADJ_VALUE (adj);
- 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);
+ flush_dialog_changes_and_save (s);
+ force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
+ populate_demo_window (s, list_elt);
- 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);
- }
-}
-
-
-
typedef struct {
- prefs_pair *pair;
+ state *state;
GtkFileSelection *widget;
} file_selection_data;
store_image_directory (GtkWidget *button, gpointer user_data)
{
file_selection_data *fsd = (file_selection_data *) user_data;
- prefs_pair *pair = fsd->pair;
+ state *s = fsd->state;
GtkFileSelection *selector = fsd->widget;
- GtkWidget *top = toplevel_widget;
- saver_preferences *p = pair->a;
- char *path = gtk_file_selection_get_filename (selector);
+ GtkWidget *top = s->toplevel_widget;
+ saver_preferences *p = &s->prefs;
+ const char *path = gtk_file_selection_get_filename (selector);
if (p->image_directory && !strcmp(p->image_directory, path))
return; /* no change */
- if (!directory_p (path))
+ /* No warning for URLs. */
+ if ((!directory_p (path)) && strncmp(path, "http://", 6))
{
char b[255];
- sprintf (b, "Error:\n\n" "Directory does not exist: \"%s\"\n", path);
- warning_dialog (GTK_WIDGET (top), b, False, 100);
+ sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
+ warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
return;
}
if (p->image_directory) free (p->image_directory);
p->image_directory = normalize_directory (path);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
(p->image_directory ? p->image_directory : ""));
- demo_write_init_file (GTK_WIDGET (top), p);
+ demo_write_init_file (s, p);
}
static void
-browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
+store_text_file (GtkWidget *button, gpointer user_data)
{
file_selection_data *fsd = (file_selection_data *) user_data;
- gtk_widget_hide (GTK_WIDGET (fsd->widget));
-}
+ state *s = fsd->state;
+ GtkFileSelection *selector = fsd->widget;
+ GtkWidget *top = s->toplevel_widget;
+ saver_preferences *p = &s->prefs;
+ const char *path = gtk_file_selection_get_filename (selector);
-static void
-browse_image_dir_ok (GtkWidget *button, gpointer user_data)
-{
- browse_image_dir_cancel (button, user_data);
- store_image_directory (button, user_data);
-}
+ if (p->text_file && !strcmp(p->text_file, path))
+ return; /* no change */
-static void
-browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
-{
- browse_image_dir_cancel (widget, user_data);
+ if (!file_p (path))
+ {
+ char b[255];
+ sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
+ warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
+ return;
+ }
+
+ if (p->text_file) free (p->text_file);
+ p->text_file = normalize_directory (path);
+
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
+ (p->text_file ? p->text_file : ""));
+ demo_write_init_file (s, p);
}
-void
-browse_image_dir_cb (GtkButton *button, gpointer user_data)
+static void
+store_text_program (GtkWidget *button, gpointer user_data)
+{
+ file_selection_data *fsd = (file_selection_data *) user_data;
+ state *s = fsd->state;
+ GtkFileSelection *selector = fsd->widget;
+ /*GtkWidget *top = s->toplevel_widget;*/
+ saver_preferences *p = &s->prefs;
+ const char *path = gtk_file_selection_get_filename (selector);
+
+ if (p->text_program && !strcmp(p->text_program, path))
+ return; /* no change */
+
+# if 0
+ if (!file_p (path))
+ {
+ char b[255];
+ sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
+ warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
+ return;
+ }
+# endif
+
+ if (p->text_program) free (p->text_program);
+ p->text_program = normalize_directory (path);
+
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
+ (p->text_program ? p->text_program : ""));
+ demo_write_init_file (s, p);
+}
+
+
+
+static void
+browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
+{
+ file_selection_data *fsd = (file_selection_data *) user_data;
+ gtk_widget_hide (GTK_WIDGET (fsd->widget));
+}
+
+static void
+browse_image_dir_ok (GtkWidget *button, gpointer user_data)
+{
+ browse_image_dir_cancel (button, user_data);
+ store_image_directory (button, user_data);
+}
+
+static void
+browse_text_file_ok (GtkWidget *button, gpointer user_data)
+{
+ browse_image_dir_cancel (button, user_data);
+ store_text_file (button, user_data);
+}
+
+static void
+browse_text_program_ok (GtkWidget *button, gpointer user_data)
+{
+ browse_image_dir_cancel (button, user_data);
+ store_text_program (button, user_data);
+}
+
+static void
+browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ browse_image_dir_cancel (widget, user_data);
+}
+
+
+G_MODULE_EXPORT void
+browse_image_dir_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;
+ state *s = global_state_kludge; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
static file_selection_data *fsd = 0;
GtkFileSelection *selector = GTK_FILE_SELECTION(
fsd = (file_selection_data *) malloc (sizeof (*fsd));
fsd->widget = selector;
- fsd->pair = pair;
+ fsd->state = s;
if (p->image_directory && *p->image_directory)
gtk_file_selection_set_filename (selector, p->image_directory);
}
-\f
-/* Populating the various widgets
- */
+G_MODULE_EXPORT void
+browse_text_file_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
+ static file_selection_data *fsd = 0;
+
+ GtkFileSelection *selector = GTK_FILE_SELECTION(
+ gtk_file_selection_new ("Please select a text file."));
+ if (!fsd)
+ fsd = (file_selection_data *) malloc (sizeof (*fsd));
+
+ fsd->widget = selector;
+ fsd->state = s;
+
+ if (p->text_file && *p->text_file)
+ gtk_file_selection_set_filename (selector, p->text_file);
+
+ gtk_signal_connect (GTK_OBJECT (selector->ok_button),
+ "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
+ (gpointer *) fsd);
+ gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
+ (gpointer *) fsd);
+ gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
+ GTK_SIGNAL_FUNC (browse_image_dir_close),
+ (gpointer *) fsd);
+
+ gtk_window_set_modal (GTK_WINDOW (selector), True);
+ gtk_widget_show (GTK_WIDGET (selector));
+}
+
+
+G_MODULE_EXPORT void
+browse_text_program_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ saver_preferences *p = &s->prefs;
+ static file_selection_data *fsd = 0;
+
+ GtkFileSelection *selector = GTK_FILE_SELECTION(
+ gtk_file_selection_new ("Please select a text-generating program."));
+
+ if (!fsd)
+ fsd = (file_selection_data *) malloc (sizeof (*fsd));
+
+ fsd->widget = selector;
+ fsd->state = s;
+
+ if (p->text_program && *p->text_program)
+ gtk_file_selection_set_filename (selector, p->text_program);
+
+ gtk_signal_connect (GTK_OBJECT (selector->ok_button),
+ "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
+ (gpointer *) fsd);
+ gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
+ "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
+ (gpointer *) fsd);
+ gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
+ GTK_SIGNAL_FUNC (browse_image_dir_close),
+ (gpointer *) fsd);
+
+ gtk_window_set_modal (GTK_WINDOW (selector), True);
+ gtk_widget_show (GTK_WIDGET (selector));
+}
+
+
+
+
+
+G_MODULE_EXPORT void
+settings_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ int list_elt = selected_list_element (s);
+
+ populate_demo_window (s, list_elt); /* reset the widget */
+ populate_popup_window (s); /* create UI on popup window */
+ gtk_widget_show (s->popup_widget);
+}
-/* Formats a `Time' into "H:MM:SS". (Time is microseconds.)
- */
static void
-format_time (char *buf, Time time)
+settings_sync_cmd_text (state *s)
+{
+# ifdef HAVE_XML
+ GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
+ char *cmd_line = get_configurator_command_line (s->cdata, False);
+ gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
+ gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
+ free (cmd_line);
+# endif /* HAVE_XML */
+}
+
+G_MODULE_EXPORT void
+settings_adv_cb (GtkButton *button, gpointer user_data)
{
- int s = time / 1000;
- unsigned int h = 0, m = 0;
- if (s >= 60)
+ state *s = global_state_kludge; /* I hate C so much... */
+ GtkNotebook *notebook =
+ GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
+
+ settings_sync_cmd_text (s);
+ gtk_notebook_set_page (notebook, 1);
+}
+
+G_MODULE_EXPORT void
+settings_std_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ GtkNotebook *notebook =
+ GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
+
+ /* Re-create UI to reflect the in-progress command-line settings. */
+ populate_popup_window (s);
+
+ gtk_notebook_set_page (notebook, 0);
+}
+
+G_MODULE_EXPORT void
+settings_reset_cb (GtkButton *button, gpointer user_data)
+{
+# ifdef HAVE_XML
+ state *s = global_state_kludge; /* I hate C so much... */
+ GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
+ char *cmd_line = get_configurator_command_line (s->cdata, True);
+ gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
+ gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
+ free (cmd_line);
+ populate_popup_window (s);
+# endif /* HAVE_XML */
+}
+
+G_MODULE_EXPORT void
+settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
+ gint page_num, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ GtkWidget *adv = name_to_widget (s, "adv_button");
+ GtkWidget *std = name_to_widget (s, "std_button");
+
+ if (page_num == 0)
{
- m += (s / 60);
- s %= 60;
+ gtk_widget_show (adv);
+ gtk_widget_hide (std);
}
- if (m >= 60)
+ else if (page_num == 1)
{
- h += (m / 60);
- m %= 60;
+ gtk_widget_hide (adv);
+ gtk_widget_show (std);
}
- sprintf (buf, "%u:%02u:%02u", h, m, s);
+ else
+ abort();
}
-/* Finds the number of the last hack to run, and makes that item be
- selected by default.
+
+G_MODULE_EXPORT void
+settings_cancel_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ gtk_widget_hide (s->popup_widget);
+}
+
+G_MODULE_EXPORT void
+settings_ok_cb (GtkButton *button, gpointer user_data)
+{
+ state *s = global_state_kludge; /* I hate C so much... */
+ GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
+ int page = gtk_notebook_get_current_page (notebook);
+
+ if (page == 0)
+ /* Regenerate the command-line from the widget contents before saving.
+ But don't do this if we're looking at the command-line page already,
+ or we will blow away what they typed... */
+ settings_sync_cmd_text (s);
+
+ flush_popup_changes_and_save (s);
+ gtk_widget_hide (s->popup_widget);
+}
+
+static gboolean
+wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ state *s = (state *) data;
+ settings_cancel_cb (0, (gpointer) s);
+ return TRUE;
+}
+
+
+\f
+/* Populating the various widgets
*/
-static void
-scroll_to_current_hack (GtkWidget *toplevel, prefs_pair *pair)
+
+
+/* Returns the number of the last hack run by the server.
+ */
+static int
+server_current_hack (void)
{
- saver_preferences *p = pair->a;
Atom type;
int format;
unsigned long nitems, bytesafter;
- CARD32 *data = 0;
+ unsigned char *dataP = 0;
Display *dpy = GDK_DISPLAY();
- int which = 0;
- GtkList *list;
+ int hack_number = -1;
if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
0, 3, False, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
- (unsigned char **) &data)
+ &dataP)
== Success
&& type == XA_INTEGER
&& nitems >= 3
- && data)
- which = (int) data[2] - 1;
+ && dataP)
+ {
+ PROP32 *data = (PROP32 *) dataP;
+ hack_number = (int) data[2] - 1;
+ }
- if (data) free (data);
+ if (dataP) XFree (dataP);
- if (which < 0)
- return;
+ return hack_number;
+}
+
+
+/* Finds the number of the last hack that was run, and makes that item be
+ selected by default.
+ */
+static void
+scroll_to_current_hack (state *s)
+{
+ saver_preferences *p = &s->prefs;
+ int hack_number = -1;
+
+ if (p->mode == ONE_HACK) /* in "one" mode, use the one */
+ hack_number = p->selected_hack;
+ if (hack_number < 0) /* otherwise, use the last-run */
+ hack_number = server_current_hack ();
+ if (hack_number < 0) /* failing that, last "one mode" */
+ hack_number = p->selected_hack;
+ if (hack_number < 0) /* failing that, newest hack. */
+ {
+ /* We should only get here if the user does not have a .xscreensaver
+ file, and the screen has not been blanked with a hack since X
+ started up: in other words, this is probably a fresh install.
+
+ Instead of just defaulting to hack #0 (in either "programs" or
+ "alphabetical" order) let's try to default to the last runnable
+ hack in the "programs" list: this is probably the hack that was
+ most recently added to the xscreensaver distribution (and so
+ it's probably the currently-coolest one!)
+ */
+ hack_number = p->screenhacks_count-1;
+ while (hack_number > 0 &&
+ ! (s->hacks_available_p[hack_number] &&
+ p->screenhacks[hack_number]->enabled_p))
+ hack_number--;
+ }
- list = GTK_LIST (name_to_widget (toplevel, "list"));
- apply_changes_and_save (toplevel);
- if (which < p->screenhacks_count)
+ if (hack_number >= 0 && hack_number < p->screenhacks_count)
{
- gtk_list_select_item (list, which);
- ensure_selected_item_visible (GTK_WIDGET (list));
- populate_demo_window (toplevel, which, pair);
+ int list_elt = s->hack_number_to_list_elt[hack_number];
+ GtkWidget *list = name_to_widget (s, "list");
+ force_list_select_item (s, list, list_elt, True);
+ populate_demo_window (s, list_elt);
}
}
-
static void
-populate_hack_list (GtkWidget *toplevel, prefs_pair *pair)
+populate_hack_list (state *s)
{
- saver_preferences *p = pair->a;
- GtkList *list = GTK_LIST (name_to_widget (toplevel, "list"));
- screenhack **hacks = p->screenhacks;
- screenhack **h;
+ Display *dpy = GDK_DISPLAY();
+#ifdef HAVE_GTK2
+ saver_preferences *p = &s->prefs;
+ GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkListStore *model;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *ren;
+ GtkTreeIter iter;
+ int i;
+
+ g_object_get (G_OBJECT (list),
+ "model", &model,
+ NULL);
+ if (!model)
+ {
+ model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
+ g_object_set (G_OBJECT (list), "model", model, NULL);
+ g_object_unref (model);
+
+ ren = gtk_cell_renderer_toggle_new ();
+ gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
+ _("Use"), ren,
+ "active", COL_ENABLED,
+ NULL);
+
+ g_signal_connect (ren, "toggled",
+ G_CALLBACK (list_checkbox_cb),
+ s);
+
+ ren = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
+ _("Screen Saver"), ren,
+ "markup", COL_NAME,
+ NULL);
+
+ g_signal_connect_after (list, "row_activated",
+ G_CALLBACK (list_activated_cb),
+ s);
+
+ selection = gtk_tree_view_get_selection (list);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (list_select_changed_cb),
+ s);
+
+ }
+
+ for (i = 0; i < s->list_count; i++)
+ {
+ int hack_number = s->list_elt_to_hack_number[i];
+ screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
+ char *pretty_name;
+ Bool available_p = (hack && s->hacks_available_p [hack_number]);
+
+ if (!hack) continue;
+
+ /* If we're to suppress uninstalled hacks, check $PATH now. */
+ if (p->ignore_uninstalled_p && !available_p)
+ continue;
+
+ pretty_name = (hack->name
+ ? strdup (hack->name)
+ : make_hack_name (dpy, hack->command));
+
+ if (!available_p)
+ {
+ /* Make the text foreground be the color of insensitive widgets
+ (but don't actually make it be insensitive, since we still
+ want to be able to click on it.)
+ */
+ GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
+ GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
+ /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
+ char *buf = (char *) malloc (strlen (pretty_name) + 100);
+
+ sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
+ /* " background=\"#%02X%02X%02X\"" */
+ ">%s</span>",
+ fg->red >> 8, fg->green >> 8, fg->blue >> 8,
+ /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
+ pretty_name);
+ free (pretty_name);
+ pretty_name = buf;
+ }
+
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COL_ENABLED, hack->enabled_p,
+ COL_NAME, pretty_name,
+ -1);
+ free (pretty_name);
+ }
+
+#else /* !HAVE_GTK2 */
- for (h = hacks; h && *h; h++)
+ saver_preferences *p = &s->prefs;
+ GtkList *list = GTK_LIST (name_to_widget (s, "list"));
+ int i;
+ for (i = 0; i < s->list_count; i++)
{
+ int hack_number = s->list_elt_to_hack_number[i];
+ screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
+
/* 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
GtkWidget *line_hbox;
GtkWidget *line_check;
GtkWidget *line_label;
+ char *pretty_name;
+ Bool available_p = (hack && s->hacks_available_p [hack_number]);
- char *pretty_name = (h[0]->name
- ? strdup (h[0]->name)
- : make_hack_name (h[0]->command));
+ if (!hack) continue;
+
+ /* If we're to suppress uninstalled hacks, check $PATH now. */
+ if (p->ignore_uninstalled_p && !available_p)
+ continue;
+
+ pretty_name = (hack->name
+ ? strdup (hack->name)
+ : make_hack_name (hack->command));
line = gtk_list_item_new ();
line_hbox = gtk_hbox_new (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);
+ hack->enabled_p);
gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
gtk_widget_show (line_check);
gtk_container_add (GTK_CONTAINER (list), line);
gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
GTK_SIGNAL_FUNC (list_doubleclick_cb),
- (gpointer) pair);
+ (gpointer) s);
gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
GTK_SIGNAL_FUNC (list_checkbox_cb),
- (gpointer) pair);
+ (gpointer) s);
-#if 0 /* #### */
- GTK_WIDGET (GTK_BIN(line)->child)->style =
- gtk_style_copy (GTK_WIDGET (text_line)->style);
-#endif
gtk_widget_show (line);
+
+ if (!available_p)
+ {
+ /* Make the widget be colored like insensitive widgets
+ (but don't actually make it be insensitive, since we
+ still want to be able to click on it.)
+ */
+ GtkRcStyle *rc_style;
+ GdkColor fg, bg;
+
+ gtk_widget_realize (GTK_WIDGET (line_label));
+
+ fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
+ bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
+
+ rc_style = gtk_rc_style_new ();
+ rc_style->fg[GTK_STATE_NORMAL] = fg;
+ rc_style->bg[GTK_STATE_NORMAL] = bg;
+ rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
+
+ gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
+ gtk_rc_style_unref (rc_style);
+ }
}
gtk_signal_connect (GTK_OBJECT (list), "select_child",
GTK_SIGNAL_FUNC (list_select_cb),
- (gpointer) pair);
+ (gpointer) s);
gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
GTK_SIGNAL_FUNC (list_unselect_cb),
- (gpointer) pair);
+ (gpointer) s);
+#endif /* !HAVE_GTK2 */
+}
+
+static void
+update_list_sensitivity (state *s)
+{
+ saver_preferences *p = &s->prefs;
+ Bool sensitive = (p->mode == RANDOM_HACKS ||
+ p->mode == RANDOM_HACKS_SAME ||
+ p->mode == ONE_HACK);
+ Bool checkable = (p->mode == RANDOM_HACKS ||
+ p->mode == RANDOM_HACKS_SAME);
+ Bool blankable = (p->mode != DONT_BLANK);
+
+#ifndef HAVE_GTK2
+ GtkWidget *head = name_to_widget (s, "col_head_hbox");
+ GtkWidget *use = name_to_widget (s, "use_col_frame");
+#endif /* HAVE_GTK2 */
+ GtkWidget *scroller = name_to_widget (s, "scroller");
+ GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
+ GtkWidget *blanker = name_to_widget (s, "blanking_table");
+
+#ifdef HAVE_GTK2
+ GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
+#else /* !HAVE_GTK2 */
+ GtkList *list = GTK_LIST (name_to_widget (s, "list"));
+ GList *kids = gtk_container_children (GTK_CONTAINER (list));
+
+ gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
+#endif /* !HAVE_GTK2 */
+ gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
+ gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
+
+#ifdef HAVE_GTK2
+ gtk_tree_view_column_set_visible (use, checkable);
+#else /* !HAVE_GTK2 */
+ if (checkable)
+ gtk_widget_show (use); /* the "Use" column header */
+ else
+ gtk_widget_hide (use);
+
+ while (kids)
+ {
+ GtkBin *line = GTK_BIN (kids->data);
+ GtkContainer *line_hbox = GTK_CONTAINER (line->child);
+ GtkWidget *line_check =
+ GTK_WIDGET (gtk_container_children (line_hbox)->data);
+
+ if (checkable)
+ gtk_widget_show (line_check);
+ else
+ gtk_widget_hide (line_check);
+
+ kids = kids->next;
+ }
+#endif /* !HAVE_GTK2 */
}
static void
-populate_prefs_page (GtkWidget *top, prefs_pair *pair)
-{
- saver_preferences *p = pair->a;
- char s[100];
-
- format_time (s, p->timeout);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "timeout_text")), s);
- format_time (s, p->cycle);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "cycle_text")), s);
- format_time (s, p->lock_timeout);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "lock_text")), s);
-
- format_time (s, p->dpms_standby);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_standby_text")),s);
- format_time (s, p->dpms_suspend);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_suspend_text")),s);
- format_time (s, p->dpms_off);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "dpms_off_text")), s);
-
- format_time (s, p->fade_seconds);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "fade_text")), s);
-
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "lock_button")),
- p->lock_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "verbose_button")),
- p->verbose_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "capture_button")),
- p->capture_stderr_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "splash_button")),
- p->splash_p);
-
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "dpms_button")),
- p->dpms_enabled_p);
-
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top,"grab_desk_button")),
- p->grab_desktop_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_video_button")),
- p->grab_video_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget(top,"grab_image_button")),
- p->random_image_p);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (top, "image_text")),
+populate_prefs_page (state *s)
+{
+ saver_preferences *p = &s->prefs;
+
+ Bool can_lock_p = True;
+
+ /* Disable all the "lock" controls if locking support was not provided
+ at compile-time, or if running on MacOS. */
+# if defined(NO_LOCKING) || defined(__APPLE__)
+ can_lock_p = False;
+# endif
+
+
+ /* If there is only one screen, the mode menu contains
+ "random" but not "random-same".
+ */
+ if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
+ p->mode = RANDOM_HACKS;
+
+
+ /* The file supports timeouts of less than a minute, but the GUI does
+ not, so throttle the values to be at least one minute (since "0" is
+ a bad rounding choice...)
+ */
+# define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
+ THROTTLE (timeout);
+ THROTTLE (cycle);
+ /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
+# undef THROTTLE
+
+# define FMT_MINUTES(NAME,N) \
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
+
+# define FMT_SECONDS(NAME,N) \
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
+
+ FMT_MINUTES ("timeout_spinbutton", p->timeout);
+ FMT_MINUTES ("cycle_spinbutton", p->cycle);
+ FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
+ FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
+ FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
+ FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
+ FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
+
+# undef FMT_MINUTES
+# undef FMT_SECONDS
+
+# define TOGGLE_ACTIVE(NAME,ACTIVEP) \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
+ (ACTIVEP))
+
+ TOGGLE_ACTIVE ("lock_button", p->lock_p);
+#if 0
+ TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
+ TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
+ TOGGLE_ACTIVE ("splash_button", p->splash_p);
+#endif
+ TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
+ TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
+ TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
+ TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
+ TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
+ TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
+ TOGGLE_ACTIVE ("fade_button", p->fade_p);
+ TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
+
+ switch (p->tmode)
+ {
+ case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
+ case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
+ case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
+ case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
+ default: TOGGLE_ACTIVE ("text_host_radio", True); break;
+ }
+
+# undef TOGGLE_ACTIVE
+
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
(p->image_directory ? p->image_directory : ""));
- gtk_widget_set_sensitive (GTK_WIDGET (name_to_widget (top, "image_text")),
+ gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
p->random_image_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top,"image_browse_button")),
+ gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
p->random_image_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "install_button")),
- p->install_cmap_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "fade_button")),
- p->fade_p);
- gtk_toggle_button_set_active (
- GTK_TOGGLE_BUTTON (name_to_widget (top, "unfade_button")),
- p->unfade_p);
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
+ (p->text_literal ? p->text_literal : ""));
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
+ (p->text_file ? p->text_file : ""));
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
+ (p->text_program ? p->text_program : ""));
+ gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
+ (p->text_url ? p->text_url : ""));
+
+ gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
+ p->tmode == TEXT_LITERAL);
+ gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
+ p->tmode == TEXT_FILE);
+ gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
+ p->tmode == TEXT_FILE);
+ gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
+ p->tmode == TEXT_PROGRAM);
+ gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
+ p->tmode == TEXT_PROGRAM);
+ gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
+ p->tmode == TEXT_URL);
+
+
+ /* Map the `saver_mode' enum to mode menu to values. */
+ {
+ GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
+ int i;
+ for (i = 0; i < countof(mode_menu_order); i++)
+ if (mode_menu_order[i] == p->mode)
+ break;
+ gtk_option_menu_set_history (opt, i);
+ update_list_sensitivity (s);
+ }
{
Bool found_any_writable_cells = False;
+ Bool fading_possible = False;
Bool dpms_supported = False;
Display *dpy = GDK_DISPLAY();
- int nscreens = ScreenCount(dpy);
+ int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
int i;
for (i = 0; i < nscreens; i++)
{
}
}
+ fading_possible = found_any_writable_cells;
#ifdef HAVE_XF86VMODE_GAMMA
- found_any_writable_cells = True; /* if we can gamma fade, go for it */
+ fading_possible = True;
#endif
#ifdef HAVE_DPMS_EXTENSION
#endif /* HAVE_DPMS_EXTENSION */
+# define SENSITIZE(NAME,SENSITIVEP) \
+ gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
+
/* Blanking and Locking
*/
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "lock_label")),
- p->lock_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "lock_text")),
- p->lock_p);
+ SENSITIZE ("lock_button", can_lock_p);
+ SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
+ SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
/* DPMS
*/
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_frame")),
- dpms_supported);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_button")),
- dpms_supported);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_standby_label")),
- dpms_supported && p->dpms_enabled_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_standby_text")),
- dpms_supported && p->dpms_enabled_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_suspend_label")),
- dpms_supported && p->dpms_enabled_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_suspend_text")),
- dpms_supported && p->dpms_enabled_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_off_label")),
- dpms_supported && p->dpms_enabled_p);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "dpms_off_text")),
- dpms_supported && p->dpms_enabled_p);
+dpms_supported=1;
+ SENSITIZE ("dpms_frame", dpms_supported);
+ SENSITIZE ("dpms_button", dpms_supported);
+ SENSITIZE ("dpms_quickoff_button", dpms_supported);
+
+ SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
+ SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
/* Colormaps
*/
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "cmap_frame")),
- found_any_writable_cells);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "install_button")),
- found_any_writable_cells);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "fade_button")),
- found_any_writable_cells);
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "unfade_button")),
- found_any_writable_cells);
-
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "fade_label")),
- (found_any_writable_cells &&
- (p->fade_p || p->unfade_p)));
- gtk_widget_set_sensitive (
- GTK_WIDGET (name_to_widget (top, "fade_text")),
- (found_any_writable_cells &&
- (p->fade_p || p->unfade_p)));
- }
+ SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
+ SENSITIZE ("install_button", found_any_writable_cells);
+ SENSITIZE ("fade_button", fading_possible);
+ SENSITIZE ("unfade_button", fading_possible);
+
+ SENSITIZE ("fade_label", (fading_possible &&
+ (p->fade_p || p->unfade_p)));
+ SENSITIZE ("fade_spinbutton", (fading_possible &&
+ (p->fade_p || p->unfade_p)));
+# undef SENSITIZE
+ }
}
static void
-sensitize_demo_widgets (GtkWidget *toplevel, Bool sensitive_p)
+populate_popup_window (state *s)
{
- const char *names[] = { "cmd_label", "cmd_text", "enabled",
- "visual", "visual_combo",
- "demo", "manual" };
- int i;
- for (i = 0; i < countof(names); i++)
+ GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
+ char *doc_string = 0;
+
+ /* #### not in Gtk 1.2
+ gtk_label_set_selectable (doc);
+ */
+
+# ifdef HAVE_XML
+ if (s->cdata)
{
- GtkWidget *w = name_to_widget (toplevel, names[i]);
- gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
+ free_conf_data (s->cdata);
+ s->cdata = 0;
}
- /* I don't know how to handle these yet... */
{
- const char *names2[] = { "cut_menu", "copy_menu", "paste_menu" };
- for (i = 0; i < countof(names2); i++)
+ saver_preferences *p = &s->prefs;
+ int list_elt = selected_list_element (s);
+ int hack_number = (list_elt >= 0 && list_elt < s->list_count
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
+ if (hack)
{
- GtkWidget *w = name_to_widget (toplevel, names2[i]);
- gtk_widget_set_sensitive (GTK_WIDGET(w), False);
+ GtkWidget *parent = name_to_widget (s, "settings_vbox");
+ GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
+ const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
+ s->cdata = load_configurator (cmd_line, s->debug_p);
+ if (s->cdata && s->cdata->widget)
+ gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
+ TRUE, TRUE, 0);
}
}
+
+ doc_string = (s->cdata
+ ? s->cdata->description
+ : 0);
+# else /* !HAVE_XML */
+ doc_string = _("Descriptions not available: no XML support compiled in.");
+# endif /* !HAVE_XML */
+
+ gtk_label_set_text (doc, (doc_string
+ ? _(doc_string)
+ : _("No description available.")));
}
-/* Even though we've given these text fields a maximum number of characters,
- their default size is still about 30 characters wide -- so measure out
- a string in their font, and resize them to just fit that.
- */
static void
-fix_text_entry_sizes (GtkWidget *toplevel)
+sensitize_demo_widgets (state *s, Bool sensitive_p)
{
- const char *names[] = { "timeout_text", "cycle_text", "lock_text",
- "dpms_standby_text", "dpms_suspend_text",
- "dpms_off_text", "fade_text" };
+ const char *names[] = { "demo", "settings",
+ "cmd_label", "cmd_text", "manual",
+ "visual", "visual_combo" };
int i;
- int width = 0;
- GtkWidget *w;
-
for (i = 0; i < countof(names); i++)
{
- w = GTK_WIDGET (name_to_widget (toplevel, names[i]));
- if (width == 0)
- width = gdk_text_width (w->style->font, "00:00:00_", 9);
- gtk_widget_set_usize (w, width, -2);
+ GtkWidget *w = name_to_widget (s, names[i]);
+ gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
}
-
- /* Now fix the size of the combo box.
- */
- w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "visual_combo"));
- w = GTK_COMBO (w)->entry;
- width = gdk_text_width (w->style->font, "PseudoColor___", 14);
- gtk_widget_set_usize (w, width, -2);
-
- /* Now fix the size of the file entry text.
- */
- w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "image_text"));
- width = gdk_text_width (w->style->font, "MMMMMMMMMMMMMM", 14);
- gtk_widget_set_usize (w, width, -2);
-
-#if 0
- /* Now fix the size of the list.
- */
- w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel), "list"));
- width = gdk_text_width (w->style->font, "nnnnnnnnnnnnnnnnnnnnnn", 22);
- gtk_widget_set_usize (w, width, -2);
-#endif
}
-
-\f
+static void
+sensitize_menu_items (state *s, Bool force_p)
+{
+ static Bool running_p = False;
+ static time_t last_checked = 0;
+ time_t now = time ((time_t *) 0);
+ const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
+ /* "demo" */ };
+ int i;
+
+ if (force_p || now > last_checked + 10) /* check every 10 seconds */
+ {
+ running_p = xscreensaver_running_p (s);
+ last_checked = time ((time_t *) 0);
+ }
+
+ for (i = 0; i < countof(names); i++)
+ {
+ GtkWidget *w = name_to_widget (s, names[i]);
+ gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
+ }
+}
+
+
+/* When the File menu is de-posted after a "Restart Daemon" command,
+ the window underneath doesn't repaint for some reason. I guess this
+ is a bug in exposure handling in GTK or GDK. This works around it.
+ */
+static void
+force_dialog_repaint (state *s)
+{
+#if 1
+ /* Tell GDK to invalidate and repaint the whole window.
+ */
+ GdkWindow *w = GET_WINDOW (s->toplevel_widget);
+ GdkRegion *region = gdk_region_new ();
+ GdkRectangle rect;
+ rect.x = rect.y = 0;
+ rect.width = rect.height = 32767;
+ gdk_region_union_with_rect (region, &rect);
+ gdk_window_invalidate_region (w, region, True);
+ gdk_region_destroy (region);
+ gdk_window_process_updates (w, True);
+#else
+ /* Force the server to send an exposure event by creating and then
+ destroying a window as a child of the top level shell.
+ */
+ Display *dpy = GDK_DISPLAY();
+ Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
+ Window w;
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (dpy, parent, &xgwa);
+ w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
+ XMapRaised (dpy, w);
+ XDestroyWindow (dpy, w);
+ XSync (dpy, False);
+#endif
+}
+
+
+/* Even though we've given these text fields a maximum number of characters,
+ their default size is still about 30 characters wide -- so measure out
+ a string in their font, and resize them to just fit that.
+ */
+static void
+fix_text_entry_sizes (state *s)
+{
+ GtkWidget *w;
+
+# if 0 /* appears no longer necessary with Gtk 1.2.10 */
+ const char * const spinbuttons[] = {
+ "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
+ "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
+ "dpms_off_spinbutton",
+ "-fade_spinbutton" };
+ int i;
+ int width = 0;
+
+ for (i = 0; i < countof(spinbuttons); i++)
+ {
+ const char *n = spinbuttons[i];
+ int cols = 4;
+ while (*n == '-') n++, cols--;
+ w = GTK_WIDGET (name_to_widget (s, n));
+ width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
+ gtk_widget_set_usize (w, width, -2);
+ }
+
+ /* Now fix the width of the combo box.
+ */
+ w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
+ w = GTK_COMBO (w)->entry;
+ width = gdk_string_width (w->style->font, "PseudoColor___");
+ gtk_widget_set_usize (w, width, -2);
+
+ /* Now fix the width of the file entry text.
+ */
+ w = GTK_WIDGET (name_to_widget (s, "image_text"));
+ width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
+ gtk_widget_set_usize (w, width, -2);
+
+ /* Now fix the width of the command line text.
+ */
+ w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
+ width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
+ gtk_widget_set_usize (w, width, -2);
+
+# endif /* 0 */
+
+ /* Now fix the height of the list widget:
+ make it default to being around 10 text-lines high instead of 4.
+ */
+ w = GTK_WIDGET (name_to_widget (s, "list"));
+ {
+ int lines = 10;
+ int height;
+ int leading = 3; /* approximate is ok... */
+ int border = 2;
+
+#ifdef HAVE_GTK2
+ PangoFontMetrics *pain =
+ pango_context_get_metrics (gtk_widget_get_pango_context (w),
+ gtk_widget_get_style (w)->font_desc,
+ gtk_get_default_language ());
+ height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
+ pango_font_metrics_get_descent (pain));
+#else /* !HAVE_GTK2 */
+ height = w->style->font->ascent + w->style->font->descent;
+#endif /* !HAVE_GTK2 */
+
+ height += leading;
+ height *= lines;
+ height += border * 2;
+ w = GTK_WIDGET (name_to_widget (s, "scroller"));
+ gtk_widget_set_usize (w, -2, height);
+ }
+}
+
+
+#ifndef HAVE_GTK2
+\f
/* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
*/
};
static void
-pixmapify_button (GtkWidget *toplevel, int down_p)
+pixmapify_button (state *s, int down_p)
{
GdkPixmap *pixmap;
GdkBitmap *mask;
GtkStyle *style;
GtkWidget *w;
- w = GTK_WIDGET (name_to_widget (GTK_WIDGET (toplevel),
- (down_p ? "next" : "prev")));
+ w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
style = gtk_widget_get_style (w);
mask = 0;
pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
static void
map_next_button_cb (GtkWidget *w, gpointer user_data)
{
- pixmapify_button (w, 1);
+ state *s = (state *) user_data;
+ pixmapify_button (s, 1);
}
static void
map_prev_button_cb (GtkWidget *w, gpointer user_data)
{
- pixmapify_button (w, 0);
+ state *s = (state *) user_data;
+ pixmapify_button (s, 0);
}
-
+#endif /* !HAVE_GTK2 */
\f
+#ifndef HAVE_GTK2
/* Work around a Gtk bug that causes label widgets to wrap text too early.
*/
gtk_widget_size_request (label, &req);
}
-
/* Feel the love. Thanks to Nat Friedman for finding this workaround.
*/
static void
-eschew_gtk_lossage (GtkWidget *toplevel)
+eschew_gtk_lossage (GtkLabel *label)
{
- GtkWidgetAuxInfo *aux_info;
- GtkWidget *label = GTK_WIDGET (name_to_widget (toplevel, "doc"));
-
- aux_info = g_new0 (GtkWidgetAuxInfo, 1);
- aux_info->width = label->allocation.width;
+ GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
+ aux_info->width = GTK_WIDGET (label)->allocation.width;
aux_info->height = -2;
aux_info->x = -1;
aux_info->y = -1;
gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
- you_are_not_a_unique_or_beautiful_snowflake, NULL);
+ GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
+ 0);
+
+ gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
- gtk_widget_queue_resize (label);
+ gtk_widget_queue_resize (GTK_WIDGET (label));
}
+#endif /* !HAVE_GTK2 */
-char *
-get_hack_blurb (screenhack *hack)
+static void
+populate_demo_window (state *s, int list_elt)
{
- char *doc_string;
- char *prog_name = strdup (hack->command);
- char *pretty_name = (hack->name
- ? strdup (hack->name)
- : make_hack_name (hack->command));
- char doc_name[255], doc_class[255];
- char *s, *s2;
+ Display *dpy = GDK_DISPLAY();
+ saver_preferences *p = &s->prefs;
+ screenhack *hack;
+ char *pretty_name;
+ GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
+ GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
+ GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
+ GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
+ GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
+
+ if (p->mode == BLANK_ONLY)
+ {
+ hack = 0;
+ pretty_name = strdup (_("Blank Screen"));
+ schedule_preview (s, 0);
+ }
+ else if (p->mode == DONT_BLANK)
+ {
+ hack = 0;
+ pretty_name = strdup (_("Screen Saver Disabled"));
+ schedule_preview (s, 0);
+ }
+ else
+ {
+ int hack_number = (list_elt >= 0 && list_elt < s->list_count
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
+
+ pretty_name = (hack
+ ? (hack->name
+ ? strdup (hack->name)
+ : make_hack_name (dpy, hack->command))
+ : 0);
+
+ if (hack)
+ schedule_preview (s, hack->command);
+ else
+ schedule_preview (s, 0);
+ }
- for (s = prog_name; *s && !isspace(*s); s++)
- ;
- *s = 0;
- s = strrchr (prog_name, '/');
- if (s) strcpy (prog_name, s+1);
+ if (!pretty_name)
+ pretty_name = strdup (_("Preview"));
- sprintf (doc_name, "hacks.%s.documentation", pretty_name);
- sprintf (doc_class, "hacks.%s.documentation", prog_name);
- free (prog_name);
- free (pretty_name);
+ gtk_frame_set_label (frame1, _(pretty_name));
+ gtk_frame_set_label (frame2, _(pretty_name));
- doc_string = get_string_resource (doc_name, doc_class);
- if (doc_string)
+ gtk_entry_set_text (cmd, (hack ? hack->command : ""));
+ gtk_entry_set_position (cmd, 0);
+
+ {
+ char title[255];
+ sprintf (title, _("%s: %.100s Settings"),
+ progclass, (pretty_name ? pretty_name : "???"));
+ gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
+ }
+
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
+ (hack
+ ? (hack->visual && *hack->visual
+ ? hack->visual
+ : _("Any"))
+ : ""));
+
+ sensitize_demo_widgets (s, (hack ? True : False));
+
+ if (pretty_name) free (pretty_name);
+
+ ensure_selected_item_visible (list);
+
+ s->_selected_list_element = list_elt;
+}
+
+
+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 char **sort_hack_cmp_names_kludge;
+static int
+sort_hack_cmp (const void *a, const void *b)
+{
+ if (a == b)
+ return 0;
+ else
+ {
+ int aa = *(int *) a;
+ int bb = *(int *) b;
+ const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
+ return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
+ (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
+ }
+}
+
+
+static void
+initialize_sort_map (state *s)
+{
+ Display *dpy = GDK_DISPLAY();
+ saver_preferences *p = &s->prefs;
+ int i, j;
+
+ if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
+ if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
+ if (s->hacks_available_p) free (s->hacks_available_p);
+
+ s->list_elt_to_hack_number = (int *)
+ calloc (sizeof(int), p->screenhacks_count + 1);
+ s->hack_number_to_list_elt = (int *)
+ calloc (sizeof(int), p->screenhacks_count + 1);
+ s->hacks_available_p = (Bool *)
+ calloc (sizeof(Bool), p->screenhacks_count + 1);
+ s->total_available = 0;
+
+ /* Check which hacks actually exist on $PATH
+ */
+ for (i = 0; i < p->screenhacks_count; i++)
+ {
+ screenhack *hack = p->screenhacks[i];
+ int on = on_path_p (hack->command) ? 1 : 0;
+ s->hacks_available_p[i] = on;
+ s->total_available += on;
+ }
+
+ /* Initialize list->hack table to unsorted mapping, omitting nonexistent
+ hacks, if desired.
+ */
+ j = 0;
+ for (i = 0; i < p->screenhacks_count; i++)
+ {
+ if (!p->ignore_uninstalled_p ||
+ s->hacks_available_p[i])
+ s->list_elt_to_hack_number[j++] = i;
+ }
+ s->list_count = j;
+
+ for (; j < p->screenhacks_count; j++)
+ s->list_elt_to_hack_number[j] = -1;
+
+
+ /* Generate list of sortable names (once)
+ */
+ sort_hack_cmp_names_kludge = (char **)
+ calloc (sizeof(char *), p->screenhacks_count);
+ for (i = 0; i < p->screenhacks_count; i++)
+ {
+ screenhack *hack = p->screenhacks[i];
+ char *name = (hack->name && *hack->name
+ ? strdup (hack->name)
+ : make_hack_name (dpy, hack->command));
+ char *str;
+ for (str = name; *str; str++)
+ *str = tolower(*str);
+ sort_hack_cmp_names_kludge[i] = name;
+ }
+
+ /* Sort list->hack map alphabetically
+ */
+ qsort (s->list_elt_to_hack_number,
+ p->screenhacks_count,
+ sizeof(*s->list_elt_to_hack_number),
+ sort_hack_cmp);
+
+ /* Free names
+ */
+ for (i = 0; i < p->screenhacks_count; i++)
+ free (sort_hack_cmp_names_kludge[i]);
+ free (sort_hack_cmp_names_kludge);
+ sort_hack_cmp_names_kludge = 0;
+
+ /* Build inverse table */
+ for (i = 0; i < p->screenhacks_count; i++)
+ {
+ int n = s->list_elt_to_hack_number[i];
+ if (n != -1)
+ s->hack_number_to_list_elt[n] = i;
+ }
+}
+
+
+static int
+maybe_reload_init_file (state *s)
+{
+ Display *dpy = GDK_DISPLAY();
+ saver_preferences *p = &s->prefs;
+ int status = 0;
+
+ static Bool reentrant_lock = False;
+ if (reentrant_lock) return 0;
+ reentrant_lock = True;
+
+ if (init_file_changed_p (p))
{
- for (s = doc_string; *s; s++)
+ const char *f = init_file_name();
+ char *b;
+ int list_elt;
+ GtkWidget *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 (s->toplevel_widget, b, D_NONE, 100);
+ free (b);
+
+ load_init_file (dpy, p);
+ initialize_sort_map (s);
+
+ list_elt = selected_list_element (s);
+ list = name_to_widget (s, "list");
+ gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
+ populate_hack_list (s);
+ force_list_select_item (s, list, list_elt, True);
+ populate_prefs_page (s);
+ populate_demo_window (s, list_elt);
+ ensure_selected_item_visible (list);
+
+ status = 1;
+ }
+
+ reentrant_lock = False;
+ return status;
+}
+
+
+\f
+/* Making the preview window have the right X visual (so that GL works.)
+ */
+
+static Visual *get_best_gl_visual (state *);
+
+static GdkVisual *
+x_visual_to_gdk_visual (Visual *xv)
+{
+ GList *gvs = gdk_list_visuals();
+ if (!xv) return gdk_visual_get_system();
+ for (; gvs; gvs = gvs->next)
+ {
+ GdkVisual *gv = (GdkVisual *) gvs->data;
+ if (xv == GDK_VISUAL_XVISUAL (gv))
+ return gv;
+ }
+ fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
+ blurb(), (unsigned long) xv->visualid);
+ abort();
+}
+
+static void
+clear_preview_window (state *s)
+{
+ GtkWidget *p;
+ GdkWindow *window;
+ GtkStyle *style;
+
+ if (!s->toplevel_widget) return; /* very early */
+ p = name_to_widget (s, "preview");
+ window = GET_WINDOW (p);
+
+ if (!window) return;
+
+ /* Flush the widget background down into the window, in case a subproc
+ has changed it. */
+ style = gtk_widget_get_style (p);
+ gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
+ gdk_window_clear (window);
+
+ {
+ int list_elt = selected_list_element (s);
+ int hack_number = (list_elt >= 0
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ Bool available_p = (hack_number >= 0
+ ? s->hacks_available_p [hack_number]
+ : True);
+ Bool nothing_p = (s->total_available < 5);
+
+#ifdef HAVE_GTK2
+ GtkWidget *notebook = name_to_widget (s, "preview_notebook");
+ gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
+ (s->running_preview_error_p
+ ? (available_p ? 1 :
+ nothing_p ? 3 : 2)
+ : 0));
+#else /* !HAVE_GTK2 */
+ if (s->running_preview_error_p)
+ {
+ const char * const lines1[] = { N_("No Preview"), N_("Available") };
+ const char * const lines2[] = { N_("Not"), N_("Installed") };
+ int nlines = countof(lines1);
+ int lh = p->style->font->ascent + p->style->font->descent;
+ int y, i;
+ gint w, h;
+
+ const char * const *lines = (available_p ? lines1 : lines2);
+
+ gdk_window_get_size (window, &w, &h);
+ y = (h - (lh * nlines)) / 2;
+ y += p->style->font->ascent;
+ for (i = 0; i < nlines; i++)
+ {
+ int sw = gdk_string_width (p->style->font, _(lines[i]));
+ int x = (w - sw) / 2;
+ gdk_draw_string (window, p->style->font,
+ p->style->fg_gc[GTK_STATE_NORMAL],
+ x, y, _(lines[i]));
+ y += lh;
+ }
+ }
+#endif /* !HAVE_GTK2 */
+ }
+
+ gdk_flush ();
+}
+
+
+static void
+reset_preview_window (state *s)
+{
+ /* On some systems (most recently, MacOS X) OpenGL programs get confused
+ when you kill one and re-start another on the same window. So maybe
+ it's best to just always destroy and recreate the preview window
+ when changing hacks, instead of always trying to reuse the same one?
+ */
+ GtkWidget *pr = name_to_widget (s, "preview");
+ if (GET_REALIZED (pr))
+ {
+ GdkWindow *window = GET_WINDOW (pr);
+ Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ Window id;
+ gtk_widget_hide (pr);
+ gtk_widget_unrealize (pr);
+ gtk_widget_realize (pr);
+ gtk_widget_show (pr);
+ id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ if (s->debug_p)
+ fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
+ (unsigned int) oid,
+ (unsigned int) id);
+ }
+}
+
+
+static void
+fix_preview_visual (state *s)
+{
+ GtkWidget *widget = name_to_widget (s, "preview");
+ Visual *xvisual = get_best_gl_visual (s);
+ GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
+ GdkVisual *dvisual = gdk_visual_get_system();
+ GdkColormap *cmap = (visual == dvisual
+ ? gdk_colormap_get_system ()
+ : gdk_colormap_new (visual, False));
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
+ (visual == dvisual ? "default" : "non-default"),
+ (xvisual ? (unsigned long) xvisual->visualid : 0L));
+
+ if (!GET_REALIZED (widget) ||
+ gtk_widget_get_visual (widget) != visual)
+ {
+ gtk_widget_unrealize (widget);
+ gtk_widget_set_visual (widget, visual);
+ gtk_widget_set_colormap (widget, cmap);
+ gtk_widget_realize (widget);
+ }
+
+ /* Set the Widget colors to be white-on-black. */
+ {
+ GdkWindow *window = GET_WINDOW (widget);
+ GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
+ GdkColormap *cmap = gtk_widget_get_colormap (widget);
+ GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
+ GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
+ GdkGC *fgc = gdk_gc_new(window);
+ GdkGC *bgc = gdk_gc_new(window);
+ if (!gdk_color_white (cmap, fg)) abort();
+ if (!gdk_color_black (cmap, bg)) abort();
+ gdk_gc_set_foreground (fgc, fg);
+ gdk_gc_set_background (fgc, bg);
+ gdk_gc_set_foreground (bgc, bg);
+ gdk_gc_set_background (bgc, fg);
+ style->fg_gc[GTK_STATE_NORMAL] = fgc;
+ style->bg_gc[GTK_STATE_NORMAL] = fgc;
+ gtk_widget_set_style (widget, style);
+
+ /* For debugging purposes, put a title on the window (so that
+ it can be easily found in the output of "xwininfo -tree".)
+ */
+ gdk_window_set_title (window, "Preview");
+ }
+
+ gtk_widget_show (widget);
+}
+
+\f
+/* Subprocesses
+ */
+
+static char *
+subproc_pretty_name (state *s)
+{
+ if (s->running_preview_cmd)
+ {
+ char *ps = strdup (s->running_preview_cmd);
+ char *ss = strchr (ps, ' ');
+ if (ss) *ss = 0;
+ ss = strrchr (ps, '/');
+ if (!ss)
+ ss = ps;
+ else
+ {
+ ss = strdup (ss+1);
+ free (ps);
+ }
+ return ss;
+ }
+ else
+ return strdup ("???");
+}
+
+
+static void
+reap_zombies (state *s)
+{
+ int wait_status = 0;
+ pid_t pid;
+ while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
+ {
+ if (s->debug_p)
+ {
+ if (pid == s->running_preview_pid)
+ {
+ char *ss = subproc_pretty_name (s);
+ fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
+ (unsigned long) pid, ss);
+ free (ss);
+ }
+ else
+ fprintf (stderr, "%s: pid %lu died\n", blurb(),
+ (unsigned long) pid);
+ }
+ }
+}
+
+
+/* Mostly lifted from driver/subprocs.c */
+static Visual *
+get_best_gl_visual (state *s)
+{
+ Display *dpy = GDK_DISPLAY();
+ pid_t forked;
+ int fds [2];
+ int in, out;
+ char buf[1024];
+
+ char *av[10];
+ int ac = 0;
+
+ av[ac++] = "xscreensaver-gl-helper";
+ av[ac] = 0;
+
+ if (pipe (fds))
+ {
+ perror ("error creating pipe:");
+ return 0;
+ }
+
+ in = fds [0];
+ out = fds [1];
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ exit (1);
+ }
+ case 0:
+ {
+ int stdout_fd = 1;
+
+ close (in); /* don't need this one */
+ close (ConnectionNumber (dpy)); /* close display fd */
+
+ if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
+ {
+ perror ("could not dup() a new stdout:");
+ return 0;
+ }
+
+ execvp (av[0], av); /* shouldn't return. */
+
+ if (errno != ENOENT)
+ {
+ /* Ignore "no such file or directory" errors, unless verbose.
+ Issue all other exec errors, though. */
+ sprintf (buf, "%s: running %s", blurb(), av[0]);
+ perror (buf);
+ }
+
+ /* Note that one must use _exit() instead of exit() in procs forked
+ off of Gtk programs -- Gtk installs an atexit handler that has a
+ copy of the X connection (which we've already closed, for safety.)
+ If one uses exit() instead of _exit(), then one sometimes gets a
+ spurious "Gdk-ERROR: Fatal IO error on X server" error message.
+ */
+ _exit (1); /* exits fork */
+ break;
+ }
+ default:
+ {
+ int result = 0;
+ int wait_status = 0;
+
+ FILE *f = fdopen (in, "r");
+ unsigned int v = 0;
+ char c;
+
+ close (out); /* don't need this one */
+
+ *buf = 0;
+ if (!fgets (buf, sizeof(buf)-1, f))
+ *buf = 0;
+ fclose (f);
+
+ /* Wait for the child to die. */
+ waitpid (-1, &wait_status, 0);
+
+ if (1 == sscanf (buf, "0x%x %c", &v, &c))
+ result = (int) v;
+
+ if (result == 0)
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s did not report a GL visual!\n",
+ blurb(), av[0]);
+ return 0;
+ }
+ else
+ {
+ Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
+ blurb(), av[0], result);
+ if (!v) abort();
+ return v;
+ }
+ }
+ }
+
+ abort();
+}
+
+
+static void
+kill_preview_subproc (state *s, Bool reset_p)
+{
+ s->running_preview_error_p = False;
+
+ reap_zombies (s);
+ clear_preview_window (s);
+
+ if (s->subproc_check_timer_id)
+ {
+ gtk_timeout_remove (s->subproc_check_timer_id);
+ s->subproc_check_timer_id = 0;
+ s->subproc_check_countdown = 0;
+ }
+
+ if (s->running_preview_pid)
+ {
+ int status = kill (s->running_preview_pid, SIGTERM);
+ char *ss = subproc_pretty_name (s);
+
+ if (status < 0)
{
- if (*s == '\n')
+ if (errno == ESRCH)
{
- /* skip over whitespace at beginning of line */
- s++;
- while (*s && (*s == ' ' || *s == '\t'))
- s++;
+ if (s->debug_p)
+ fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
+ blurb(), (unsigned long) s->running_preview_pid, ss);
}
- else if (*s == ' ' || *s == '\t')
+ else
{
- /* compress all other horizontal whitespace. */
- *s = ' ';
- s++;
- for (s2 = s; *s2 && (*s2 == ' ' || *s2 == '\t'); s2++)
- ;
- if (s2 > s) strcpy (s, s2);
- s--;
+ char buf [1024];
+ sprintf (buf, "%s: couldn't kill pid %lu (%s)",
+ blurb(), (unsigned long) s->running_preview_pid, ss);
+ perror (buf);
}
}
+ else {
+ int endstatus;
+ waitpid(s->running_preview_pid, &endstatus, 0);
+ if (s->debug_p)
+ fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
+ (unsigned long) s->running_preview_pid, ss);
+ }
+
+ free (ss);
+ s->running_preview_pid = 0;
+ if (s->running_preview_cmd) free (s->running_preview_cmd);
+ s->running_preview_cmd = 0;
+ }
+
+ reap_zombies (s);
+
+ if (reset_p)
+ {
+ reset_preview_window (s);
+ clear_preview_window (s);
+ }
+}
+
+
+/* Immediately and unconditionally launches the given process,
+ after appending the -window-id option; sets running_preview_pid.
+ */
+static void
+launch_preview_subproc (state *s)
+{
+ saver_preferences *p = &s->prefs;
+ Window id;
+ char *new_cmd = 0;
+ pid_t forked;
+ const char *cmd = s->desired_preview_cmd;
+
+ GtkWidget *pr = name_to_widget (s, "preview");
+ GdkWindow *window;
+
+ reset_preview_window (s);
+
+ window = GET_WINDOW (pr);
+
+ s->running_preview_error_p = False;
+
+ if (s->preview_suppressed_p)
+ {
+ kill_preview_subproc (s, False);
+ goto DONE;
+ }
+
+ new_cmd = malloc (strlen (cmd) + 40);
+
+ id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ if (id == 0)
+ {
+ /* No window id? No command to run. */
+ free (new_cmd);
+ new_cmd = 0;
+ }
+ else
+ {
+ strcpy (new_cmd, cmd);
+ sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
+ (unsigned int) id);
+ }
+
+ kill_preview_subproc (s, False);
+ if (! new_cmd)
+ {
+ s->running_preview_error_p = True;
+ clear_preview_window (s);
+ goto DONE;
+ }
+
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ char buf[255];
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ s->running_preview_error_p = True;
+ goto DONE;
+ break;
+ }
+ case 0:
+ {
+ close (ConnectionNumber (GDK_DISPLAY()));
+
+ hack_subproc_environment (id, s->debug_p);
+
+ usleep (250000); /* pause for 1/4th second before launching, to give
+ the previous program time to die and flush its X
+ buffer, so we don't get leftover turds on the
+ window. */
+
+ exec_command (p->shell, new_cmd, p->nice_inferior);
+ /* Don't bother printing an error message when we are unable to
+ exec subprocesses; we handle that by polling the pid later.
+
+ Note that one must use _exit() instead of exit() in procs forked
+ off of Gtk programs -- Gtk installs an atexit handler that has a
+ copy of the X connection (which we've already closed, for safety.)
+ If one uses exit() instead of _exit(), then one sometimes gets a
+ spurious "Gdk-ERROR: Fatal IO error on X server" error message.
+ */
+ _exit (1); /* exits child fork */
+ break;
+
+ default:
+
+ if (s->running_preview_cmd) free (s->running_preview_cmd);
+ s->running_preview_cmd = strdup (s->desired_preview_cmd);
+ s->running_preview_pid = forked;
+
+ if (s->debug_p)
+ {
+ char *ss = subproc_pretty_name (s);
+ fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
+ (unsigned long) forked, ss);
+ free (ss);
+ }
+ break;
+ }
+ }
+
+ schedule_preview_check (s);
+
+ DONE:
+ if (new_cmd) free (new_cmd);
+ new_cmd = 0;
+}
+
+
+/* Modify $DISPLAY and $PATH for the benefit of subprocesses.
+ */
+static void
+hack_environment (state *s)
+{
+ static const char *def_path =
+# ifdef DEFAULT_PATH_PREFIX
+ DEFAULT_PATH_PREFIX;
+# else
+ "";
+# endif
+
+ Display *dpy = GDK_DISPLAY();
+ const char *odpy = DisplayString (dpy);
+ char *ndpy = (char *) malloc(strlen(odpy) + 20);
+ strcpy (ndpy, "DISPLAY=");
+ strcat (ndpy, odpy);
+ if (putenv (ndpy))
+ abort ();
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s\n", blurb(), ndpy);
+
+ /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
+ 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
+ So we must leak it (and/or the previous setting). Yay.
+ */
+
+ if (def_path && *def_path)
+ {
+ const char *opath = getenv("PATH");
+ char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
+ strcpy (npath, "PATH=");
+ strcat (npath, def_path);
+ strcat (npath, ":");
+ strcat (npath, opath);
+
+ if (putenv (npath))
+ abort ();
+ /* do not free(npath) -- see above */
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
+ }
+}
+
+
+static void
+hack_subproc_environment (Window preview_window_id, Bool debug_p)
+{
+ /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
+ necessary yet, but it will make programs work if we had invoked
+ them with "-root" and not with "-window-id" -- which, of course,
+ doesn't happen.
+ */
+ char *nssw = (char *) malloc (40);
+ sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
+
+ /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
+ any more, right? It's not Posix, but everyone seems to have it. */
+ if (putenv (nssw))
+ abort ();
+
+ if (debug_p)
+ fprintf (stderr, "%s: %s\n", blurb(), nssw);
+
+ /* do not free(nssw) -- see above */
+}
+
+
+/* Called from a timer:
+ Launches the currently-chosen subprocess, if it's not already running.
+ If there's a different process running, kills it.
+ */
+static int
+update_subproc_timer (gpointer data)
+{
+ state *s = (state *) data;
+ if (! s->desired_preview_cmd)
+ kill_preview_subproc (s, True);
+ else if (!s->running_preview_cmd ||
+ !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
+ launch_preview_subproc (s);
+
+ s->subproc_timer_id = 0;
+ return FALSE; /* do not re-execute timer */
+}
+
+static int
+settings_timer (gpointer data)
+{
+ settings_cb (0, 0);
+ return FALSE;
+}
+
+
+/* Call this when you think you might want a preview process running.
+ It will set a timer that will actually launch that program a second
+ from now, if you haven't changed your mind (to avoid double-click
+ spazzing, etc.) `cmd' may be null meaning "no process".
+ */
+static void
+schedule_preview (state *s, const char *cmd)
+{
+ int delay = 1000 * 0.5; /* 1/2 second hysteresis */
+
+ if (s->debug_p)
+ {
+ if (cmd)
+ fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
+ else
+ fprintf (stderr, "%s: scheduling preview death\n", blurb());
+ }
+
+ if (s->desired_preview_cmd) free (s->desired_preview_cmd);
+ s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
+
+ if (s->subproc_timer_id)
+ gtk_timeout_remove (s->subproc_timer_id);
+ s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
+}
- while (*s && isspace (*s)) /* Strip trailing whitespace */
- *(--s) = 0;
- /* Delete whitespace at end of each line. */
- for (; s > doc_string; s--)
- if (*s == '\n' && (s[-1] == ' ' || s[-1] == '\t'))
- {
- for (s2 = s-1;
- s2 > doc_string && (*s2 == ' ' || *s2 == '\t');
- s2--)
- ;
- s2++;
- if (s2 < s) strcpy (s2, s);
- s = s2;
- }
-
- /* Delete leading blank lines. */
- for (s = doc_string; *s == '\n'; s++)
- ;
- if (s > doc_string) strcpy (doc_string, s);
+/* Called from a timer:
+ Checks to see if the subproc that should be running, actually is.
+ */
+static int
+check_subproc_timer (gpointer data)
+{
+ state *s = (state *) data;
+ Bool again_p = True;
+
+ if (s->running_preview_error_p || /* already dead */
+ s->running_preview_pid <= 0)
+ {
+ again_p = False;
}
else
{
- static int doc_installed = 0;
- if (doc_installed == 0)
+ int status;
+ reap_zombies (s);
+ status = kill (s->running_preview_pid, 0);
+ if (status < 0 && errno == ESRCH)
+ s->running_preview_error_p = True;
+
+ if (s->debug_p)
{
- if (get_boolean_resource ("hacks.documentation.isInstalled",
- "hacks.documentation.isInstalled"))
- doc_installed = 1;
- else
- doc_installed = -1;
+ char *ss = subproc_pretty_name (s);
+ fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
+ (unsigned long) s->running_preview_pid, ss,
+ (s->running_preview_error_p ? "dead" : "alive"));
+ free (ss);
}
- if (doc_installed < 0)
- doc_string =
- strdup ("Error:\n\n"
- "The documentation strings do not appear to be "
- "installed. This is probably because there is "
- "an \"XScreenSaver\" app-defaults file installed "
- "that is from an older version of the program. "
- "To fix this problem, delete that file, or "
- "install a current version (either will work.)");
- else
- doc_string = strdup ("");
+ if (s->running_preview_error_p)
+ {
+ clear_preview_window (s);
+ again_p = False;
+ }
}
- return doc_string;
+ /* Otherwise, it's currently alive. We might be checking again, or we
+ might be satisfied. */
+
+ if (--s->subproc_check_countdown <= 0)
+ again_p = False;
+
+ if (again_p)
+ return TRUE; /* re-execute timer */
+ else
+ {
+ s->subproc_check_timer_id = 0;
+ s->subproc_check_countdown = 0;
+ return FALSE; /* do not re-execute timer */
+ }
}
+/* Call this just after launching a subprocess.
+ This sets a timer that will, five times a second for two seconds,
+ check whether the program is still running. The assumption here
+ is that if the process didn't stay up for more than a couple of
+ seconds, then either the program doesn't exist, or it doesn't
+ take a -window-id argument.
+ */
static void
-populate_demo_window (GtkWidget *toplevel, int which, prefs_pair *pair)
-{
- saver_preferences *p = pair->a;
- 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"));
- GtkToggleButton *enabled =
- GTK_TOGGLE_BUTTON (name_to_widget (toplevel, "enabled"));
- GtkCombo *vis = GTK_COMBO (name_to_widget (toplevel, "visual_combo"));
-
- char *pretty_name = (hack
- ? (hack->name
- ? strdup (hack->name)
- : make_hack_name (hack->command))
- : 0);
- char *doc_string = hack ? get_hack_blurb (hack) : 0;
-
- gtk_frame_set_label (frame, (pretty_name ? pretty_name : ""));
- 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--;
+schedule_preview_check (state *s)
+{
+ int seconds = 2;
+ int ticks = 5;
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: scheduling check\n", blurb());
+
+ if (s->subproc_check_timer_id)
+ gtk_timeout_remove (s->subproc_check_timer_id);
+ s->subproc_check_timer_id =
+ gtk_timeout_add (1000 / ticks,
+ check_subproc_timer, (gpointer) s);
+ s->subproc_check_countdown = ticks * seconds;
+}
- gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
- (hack
- ? (hack->visual && *hack->visual
- ? hack->visual
- : "Any")
- : ""));
- gtk_container_resize_children (GTK_CONTAINER (GTK_WIDGET (doc)->parent));
+static Bool
+screen_blanked_p (void)
+{
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *dataP = 0;
+ Display *dpy = GDK_DISPLAY();
+ Bool blanked_p = False;
- sensitize_demo_widgets (toplevel, (hack ? True : False));
+ if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
+ XA_SCREENSAVER_STATUS,
+ 0, 3, False, XA_INTEGER,
+ &type, &format, &nitems, &bytesafter,
+ &dataP)
+ == Success
+ && type == XA_INTEGER
+ && nitems >= 3
+ && dataP)
+ {
+ Atom *data = (Atom *) dataP;
+ blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
+ }
- if (pretty_name) free (pretty_name);
- if (doc_string) free (doc_string);
+ if (dataP) XFree (dataP);
- _selected_hack_number = which;
+ return blanked_p;
}
-
-static void
-widget_deleter (GtkWidget *widget, gpointer data)
+/* Wake up every now and then and see if the screen is blanked.
+ If it is, kill off the small-window demo -- no point in wasting
+ cycles by running two screensavers at once...
+ */
+static int
+check_blanked_timer (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!
+ state *s = (state *) data;
+ Bool blanked_p = screen_blanked_p ();
+ if (blanked_p && s->running_preview_pid)
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
+ kill_preview_subproc (s, True);
+ }
- #### 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
+ return True; /* re-execute timer */
}
+/* How many screens are there (including Xinerama.)
+ */
static int
-maybe_reload_init_file (GtkWidget *widget, prefs_pair *pair)
+screen_count (Display *dpy)
{
- 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))
+ int nscreens = ScreenCount(dpy);
+# ifdef HAVE_XINERAMA
+ if (nscreens <= 1)
{
- 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;
+ int event_number, error_number;
+ if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
+ XineramaIsActive (dpy))
+ {
+ XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
+ if (xsi) XFree (xsi);
+ }
}
+# endif /* HAVE_XINERAMA */
- reentrant_lock = False;
- return status;
+ return nscreens;
}
-
\f
/* Setting window manager icon
*/
init_icon (GdkWindow *window)
{
GdkBitmap *mask = 0;
- GdkColor transp;
GdkPixmap *pixmap =
- gdk_pixmap_create_from_xpm_d (window, &mask, &transp,
+ gdk_pixmap_create_from_xpm_d (window, &mask, 0,
(gchar **) logo_50_xpm);
if (pixmap)
gdk_window_set_icon (window, 0, pixmap, mask);
#endif
+static Window
+gnome_screensaver_window (Screen *screen)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Window root = RootWindowOfScreen (screen);
+ Window parent, *kids;
+ unsigned int nkids;
+ Window gnome_window = 0;
+ int i;
+
+ if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
+ abort ();
+ for (i = 0; i < nkids; i++)
+ {
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *name;
+ if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
+ False, XA_STRING, &type, &format, &nitems,
+ &bytesafter, &name)
+ == Success
+ && type != None
+ && !strcmp ((char *) name, "gnome-screensaver"))
+ {
+ gnome_window = kids[i];
+ break;
+ }
+ }
+
+ if (kids) XFree ((char *) kids);
+ return gnome_window;
+}
+
+static Bool
+gnome_screensaver_active_p (void)
+{
+ Display *dpy = GDK_DISPLAY();
+ Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
+ return (w ? True : False);
+}
+
+static void
+kill_gnome_screensaver (void)
+{
+ Display *dpy = GDK_DISPLAY();
+ Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
+ if (w) XKillClient (dpy, (XID) w);
+}
+
+static Bool
+kde_screensaver_active_p (void)
+{
+ FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
+ "r");
+ char buf[255];
+ fgets (buf, sizeof(buf)-1, p);
+ pclose (p);
+ if (!strcmp (buf, "true\n"))
+ return True;
+ else
+ return False;
+}
+
+static void
+kill_kde_screensaver (void)
+{
+ system ("dcop kdesktop KScreensaverIface enable false");
+}
+
+
static void
-the_network_is_not_the_computer (GtkWidget *parent)
+the_network_is_not_the_computer (state *s)
{
Display *dpy = GDK_DISPLAY();
- char *rversion, *ruser, *rhost;
+ char *rversion = 0, *ruser = 0, *rhost = 0;
char *luser, *lhost;
char *msg = 0;
struct passwd *p = getpwuid (getuid ());
if (!rversion || !*rversion)
{
sprintf (msg,
- "Warning:\n\n"
- "The XScreenSaver daemon doesn't seem to be running\n"
- "on display \"%s\". Launch it now?",
+ _("Warning:\n\n"
+ "The XScreenSaver daemon doesn't seem to be running\n"
+ "on display \"%s\". Launch it now?"),
d);
}
else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
/* Warn that the two processes are running as different users.
*/
sprintf(msg,
- "Warning:\n\n"
+ _("Warning:\n\n"
"%s is running as user \"%s\" on host \"%s\".\n"
"But the xscreensaver managing display \"%s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"You should either re-run %s as \"%s\", or re-run\n"
"xscreensaver as \"%s\".\n"
"\n"
- "Restart the xscreensaver daemon now?\n",
+ "Restart the xscreensaver daemon now?\n"),
progname, luser, lhost,
d,
(ruser ? ruser : "???"), (rhost ? rhost : "???"),
/* Warn that the two processes are running on different hosts.
*/
sprintf (msg,
- "Warning:\n\n"
+ _("Warning:\n\n"
"%s is running as user \"%s\" on host \"%s\".\n"
"But the xscreensaver managing display \"%s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"if they don't see the same ~%s/.xscreensaver file) then\n"
"%s won't work right.\n"
"\n"
- "Restart the daemon on \"%s\" as \"%s\" now?\n",
+ "Restart the daemon on \"%s\" as \"%s\" now?\n"),
progname, luser, lhost,
d,
(ruser ? ruser : "???"), (rhost ? rhost : "???"),
progname,
lhost, luser);
}
- else if (!!strcmp (rversion, short_version))
+ else if (!!strcmp (rversion, s->short_version))
{
/* Warn that the version numbers don't match.
*/
sprintf (msg,
- "Warning:\n\n"
+ _("Warning:\n\n"
"This is %s version %s.\n"
"But the xscreensaver managing display \"%s\"\n"
"is version %s. This could cause problems.\n"
- "\n"
- "Restart the xscreensaver daemon now?\n",
- progname, short_version,
+ "\n"
+ "Restart the xscreensaver daemon now?\n"),
+ progname, s->short_version,
d,
rversion);
}
if (*msg)
- warning_dialog (parent, msg, True, 1);
+ warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
+ if (rversion) free (rversion);
+ if (ruser) free (ruser);
+ if (rhost) free (rhost);
free (msg);
+ msg = 0;
+
+ /* Note: since these dialogs are not modal, they will stack up.
+ So we do this check *after* popping up the "xscreensaver is not
+ running" dialog so that these are on top. Good enough.
+ */
+
+ if (gnome_screensaver_active_p ())
+ warning_dialog (s->toplevel_widget,
+ _("Warning:\n\n"
+ "The GNOME screensaver daemon appears to be running.\n"
+ "It must be stopped for XScreenSaver to work properly.\n"
+ "\n"
+ "Stop the GNOME screen saver daemon now?\n"),
+ D_GNOME, 1);
+
+ if (kde_screensaver_active_p ())
+ warning_dialog (s->toplevel_widget,
+ _("Warning:\n\n"
+ "The KDE screen saver daemon appears to be running.\n"
+ "It must be stopped for XScreenSaver to work properly.\n"
+ "\n"
+ "Stop the KDE screen saver daemon now?\n"),
+ D_KDE, 1);
}
static int
demo_ehandler (Display *dpy, XErrorEvent *error)
{
- fprintf (stderr, "\nX error in %s:\n", progname);
- if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
- exit (-1);
- else
- fprintf (stderr, " (nonfatal.)\n");
+ state *s = global_state_kludge; /* I hate C so much... */
+ fprintf (stderr, "\nX error in %s:\n", blurb());
+ XmuPrintDefaultErrorMessage (dpy, error, stderr);
+ kill_preview_subproc (s, False);
+ exit (-1);
return 0;
}
if (strstr (message, "unknown window"))
return;
- fprintf (stderr, "%s: %s-%s: %s%s", blurb(), log_domain,
+ fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
+ (log_domain ? log_domain : progclass),
(log_level == G_LOG_LEVEL_ERROR ? "error" :
log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
log_level == G_LOG_LEVEL_WARNING ? "warning" :
}
+#ifdef __GNUC__
+ __extension__ /* shut up about "string length is greater than the length
+ ISO C89 compilers are required to support" when including
+ the .ad file... */
+#endif
+
+STFU
static char *defaults[] = {
#include "XScreenSaver_ad.h"
0
#endif /* HAVE_CRAPPLET */
#endif /* 0 */
-#define USAGE() \
- fprintf (stderr, "usage: %s [ -display dpy-string ] [ -prefs ]\n", \
- real_progname)
+const char *usage = "[--display dpy] [--prefs | --settings]"
+# ifdef HAVE_CRAPPLET
+ " [--crapplet]"
+# endif
+ "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
+
+static void
+map_popup_window_cb (GtkWidget *w, gpointer user_data)
+{
+ state *s = (state *) user_data;
+ Boolean oi = s->initializing_p;
+#ifndef HAVE_GTK2
+ GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
+#endif
+ s->initializing_p = True;
+#ifndef HAVE_GTK2
+ eschew_gtk_lossage (label);
+#endif
+ s->initializing_p = oi;
+}
+#if 0
static void
-map_window_cb (GtkWidget *w, gpointer user_data)
+print_widget_tree (GtkWidget *w, int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ fprintf (stderr, " ");
+ fprintf (stderr, "%s\n", gtk_widget_get_name (w));
+
+ if (GTK_IS_LIST (w))
+ {
+ for (i = 0; i < depth+1; i++)
+ fprintf (stderr, " ");
+ fprintf (stderr, "...list kids...\n");
+ }
+ else if (GTK_IS_CONTAINER (w))
+ {
+ GList *kids = gtk_container_children (GTK_CONTAINER (w));
+ while (kids)
+ {
+ print_widget_tree (GTK_WIDGET (kids->data), depth+1);
+ kids = kids->next;
+ }
+ }
+}
+#endif /* 0 */
+
+static int
+delayed_scroll_kludge (gpointer data)
+{
+ state *s = (state *) data;
+ GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
+ ensure_selected_item_visible (w);
+
+ /* Oh, this is just fucking lovely, too. */
+ w = GTK_WIDGET (name_to_widget (s, "preview"));
+ gtk_widget_hide (w);
+ gtk_widget_show (w);
+
+ return FALSE; /* do not re-execute timer */
+}
+
+#ifdef HAVE_GTK2
+
+static GtkWidget *
+create_xscreensaver_demo (void)
{
- 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;
+ GtkWidget *nb;
+
+ nb = name_to_widget (global_state_kludge, "preview_notebook");
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
+
+ return name_to_widget (global_state_kludge, "xscreensaver_demo");
+}
+
+static GtkWidget *
+create_xscreensaver_settings_dialog (void)
+{
+ GtkWidget *w, *box;
+
+ box = name_to_widget (global_state_kludge, "dialog_action_area");
+
+ w = name_to_widget (global_state_kludge, "adv_button");
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
+
+ w = name_to_widget (global_state_kludge, "std_button");
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
+
+ return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
}
+#endif /* HAVE_GTK2 */
int
main (int argc, char **argv)
{
XtAppContext app;
- prefs_pair Pair, *pair;
- saver_preferences P, P2, *p, *p2;
- Bool prefs = False;
+ state S, *s;
+ saver_preferences *p;
+ Bool prefs_p = False;
+ Bool settings_p = False;
int i;
Display *dpy;
Widget toplevel_shell;
- GtkWidget *gtk_window;
char *real_progname = argv[0];
- char *s;
+ char *window_title;
+ char *geom = 0;
+ Bool crapplet_p = False;
+ char *str;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+
+# ifdef HAVE_GTK2
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# else /* !HAVE_GTK2 */
+ if (!setlocale (LC_ALL, ""))
+ fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
+# endif /* !HAVE_GTK2 */
- initializing_p = True;
+#endif /* ENABLE_NLS */
- s = strrchr (real_progname, '/');
- if (s) real_progname = s+1;
+ str = strrchr (real_progname, '/');
+ if (str) real_progname = str+1;
- p = &P;
- p2 = &P2;
- pair = &Pair;
- pair->a = p;
- pair->b = p2;
- memset (p, 0, sizeof (*p));
- memset (p2, 0, sizeof (*p2));
+ s = &S;
+ memset (s, 0, sizeof(*s));
+ s->initializing_p = True;
+ p = &s->prefs;
- global_prefs_pair = pair; /* I hate C so much... */
+ global_state_kludge = s; /* I hate C so much... */
progname = real_progname;
- short_version = (char *) malloc (5);
- memcpy (short_version, screensaver_id + 17, 4);
- short_version [4] = 0;
+ s->short_version = (char *) malloc (5);
+ memcpy (s->short_version, screensaver_id + 17, 4);
+ s->short_version [4] = 0;
/* Register our error message logger for every ``log domain'' known.
for all of the domains that seem to be in use.
*/
{
- const char * const domains[] = { "Gtk", "Gdk", "GLib", "GModule",
- "GThread", "Gnome", "GnomeUI", 0 };
- for (i = 0; domains[i]; i++)
+ const char * const domains[] = { 0,
+ "Gtk", "Gdk", "GLib", "GModule",
+ "GThread", "Gnome", "GnomeUI" };
+ for (i = 0; i < countof(domains); i++)
g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
}
+#ifdef DEFAULT_ICONDIR /* from -D on compile line */
+# ifndef HAVE_GTK2
+ {
+ const char *dir = DEFAULT_ICONDIR;
+ if (*dir) add_pixmap_directory (dir);
+ }
+# endif /* !HAVE_GTK2 */
+#endif /* DEFAULT_ICONDIR */
+
/* This is gross, but Gtk understands --display and not -display...
*/
for (i = 1; i < argc; i++)
/* 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);
- USAGE ();
- exit (1);
-# endif /* !HAVE_CRAPPLET */
- }
+ {
+ if (argv[i] &&
+ (!strcmp(argv[i], "--crapplet") ||
+ !strcmp(argv[i], "--capplet")))
+ {
+# if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
+ 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 && !HAVE_GTK2 */
+ fprintf (stderr, "%s: not compiled with --crapplet support\n",
+ real_progname);
+ fprintf (stderr, "%s: %s\n", real_progname, usage);
+ exit (1);
+# endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
+ }
+ else if (argv[i] &&
+ (!strcmp(argv[i], "--debug") ||
+ !strcmp(argv[i], "-debug") ||
+ !strcmp(argv[i], "-d")))
+ {
+ int j;
+ s->debug_p = True;
+ for (j = i; j < argc; j++) /* remove it from the list */
+ argv[j] = argv[j+1];
+ argc--;
+ i--;
+ }
+ else if (argv[i] &&
+ argc > i+1 &&
+ *argv[i+1] &&
+ (!strcmp(argv[i], "-geometry") ||
+ !strcmp(argv[i], "-geom") ||
+ !strcmp(argv[i], "-geo") ||
+ !strcmp(argv[i], "-g")))
+ {
+ int j;
+ geom = argv[i+1];
+ for (j = i; j < argc; j++) /* remove them from the list */
+ argv[j] = argv[j+2];
+ argc -= 2;
+ i -= 2;
+ }
+ else if (argv[i] &&
+ argc > i+1 &&
+ *argv[i+1] &&
+ (!strcmp(argv[i], "--configdir")))
+ {
+ int j;
+ struct stat st;
+ hack_configuration_path = argv[i+1];
+ for (j = i; j < argc; j++) /* remove them from the list */
+ argv[j] = argv[j+2];
+ argc -= 2;
+ i -= 2;
+
+ if (0 != stat (hack_configuration_path, &st))
+ {
+ char buf[255];
+ sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
+ perror (buf);
+ exit (1);
+ }
+ else if (!S_ISDIR (st.st_mode))
+ {
+ fprintf (stderr, "%s: not a directory: %s\n",
+ blurb(), hack_configuration_path);
+ exit (1);
+ }
+ }
+ }
+
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: using config directory \"%s\"\n",
+ progname, hack_configuration_path);
+
/* Let Gtk open the X connection, then initialize Xt to use that
same connection. Doctor Frankenstein would be proud.
GnomeClientFlags flags = 0;
int init_results = gnome_capplet_init ("screensaver-properties",
- short_version,
+ s->short_version,
argc, argv, NULL, 0, NULL);
/* init_results is:
0 upon successful initialization;
XtGetApplicationNameAndClass (dpy, &progname, &progclass);
XSetErrorHandler (demo_ehandler);
+ /* Let's just ignore these. They seem to confuse Irix Gtk... */
+ signal (SIGPIPE, SIG_IGN);
/* After doing Xt-style command-line processing, complain about any
unrecognized command-line arguments.
*/
for (i = 1; i < argc; i++)
{
- char *s = argv[i];
- if (s[0] == '-' && s[1] == '-')
- s++;
- if (!strcmp (s, "-prefs"))
- prefs = True;
+ char *str = argv[i];
+ if (str[0] == '-' && str[1] == '-')
+ str++;
+ if (!strcmp (str, "-prefs"))
+ prefs_p = True;
+ else if (!strcmp (str, "-settings"))
+ settings_p = 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, "%s: unknown option: %s\n", real_progname, argv[i]);
- USAGE ();
+ fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
+ argv[i]);
+ fprintf (stderr, "%s: %s\n", real_progname, usage);
exit (1);
}
}
was in argv[0].
*/
p->db = db;
- load_init_file (p);
- *p2 = *p;
+ s->nscreens = screen_count (dpy);
+
+ hack_environment (s); /* must be before initialize_sort_map() */
+
+ load_init_file (dpy, p);
+ initialize_sort_map (s);
/* Now that Xt has been initialized, and the resources have been read,
we can set our `progname' variable to something more in line with
/* Create the window and all its widgets.
*/
- gtk_window = create_xscreensaver_demo ();
- toplevel_widget = gtk_window;
+ s->base_widget = create_xscreensaver_demo ();
+ s->popup_widget = create_xscreensaver_settings_dialog ();
+ s->toplevel_widget = s->base_widget;
- /* Set the window's title. */
+
+ /* Set the main window's title. */
{
- char title[255];
+ char *base_title = _("Screensaver Preferences");
char *v = (char *) strdup(strchr(screensaver_id, ' '));
char *s1, *s2, *s3, *s4;
s1 = (char *) strchr(v, ' '); s1++;
s4 = (char *) strchr(s3, ')');
*s2 = 0;
*s4 = 0;
- sprintf (title, "%.50s %.50s, %.50s", progclass, s1, s3);
- gtk_window_set_title (GTK_WINDOW (gtk_window), title);
+
+ window_title = (char *) malloc (strlen (base_title) +
+ strlen (progclass) +
+ strlen (s1) + strlen (s3) +
+ 100);
+ sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
+ gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
+ gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
free (v);
}
+ /* Adjust the (invisible) notebooks on the popup dialog... */
+ {
+ GtkNotebook *notebook =
+ GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
+ GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
+ int page = 0;
+
+# ifdef HAVE_XML
+ gtk_widget_hide (std);
+# else /* !HAVE_XML */
+ /* Make the advanced page be the only one available. */
+ gtk_widget_set_sensitive (std, False);
+ std = GTK_WIDGET (name_to_widget (s, "adv_button"));
+ gtk_widget_hide (std);
+ std = GTK_WIDGET (name_to_widget (s, "reset_button"));
+ gtk_widget_hide (std);
+ page = 1;
+# endif /* !HAVE_XML */
+
+ gtk_notebook_set_page (notebook, page);
+ gtk_notebook_set_show_tabs (notebook, False);
+ }
+
/* Various other widget initializations...
*/
- gtk_signal_connect (GTK_OBJECT (gtk_window), "delete_event",
- GTK_SIGNAL_FUNC (wm_close_cb), NULL);
-
- populate_hack_list (gtk_window, pair);
- populate_prefs_page (gtk_window, pair);
- sensitize_demo_widgets (gtk_window, False);
- fix_text_entry_sizes (gtk_window);
- scroll_to_current_hack (gtk_window, pair);
-
- 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);
+ gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
+ GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
+ (gpointer) s);
+ gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
+ GTK_SIGNAL_FUNC (wm_popup_close_cb),
+ (gpointer) s);
+
+ populate_hack_list (s);
+ populate_prefs_page (s);
+ sensitize_demo_widgets (s, False);
+ fix_text_entry_sizes (s);
+ scroll_to_current_hack (s);
+
+ gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
+ "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
+ (gpointer) s);
+
+#ifndef HAVE_GTK2
+ gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
+ "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
+ (gpointer) s);
+ gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
+ "map", GTK_SIGNAL_FUNC(map_next_button_cb),
+ (gpointer) s);
+#endif /* !HAVE_GTK2 */
+
+ /* Hook up callbacks to the items on the mode menu. */
+ {
+ GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
+ GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
+ GList *kids = gtk_container_children (GTK_CONTAINER (menu));
+ int i;
+ for (i = 0; kids; kids = kids->next, i++)
+ {
+ gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
+ GTK_SIGNAL_FUNC (mode_menu_item_cb),
+ (gpointer) s);
+
+ /* The "random-same" mode menu item does not appear unless
+ there are multple screens.
+ */
+ if (s->nscreens <= 1 &&
+ mode_menu_order[i] == RANDOM_HACKS_SAME)
+ gtk_widget_hide (GTK_WIDGET (kids->data));
+ }
+
+ if (s->nscreens <= 1) /* recompute option-menu size */
+ {
+ gtk_widget_unrealize (GTK_WIDGET (menu));
+ gtk_widget_realize (GTK_WIDGET (menu));
+ }
+ }
/* Handle the -prefs command-line argument. */
- if (prefs)
+ if (prefs_p)
{
GtkNotebook *notebook =
- GTK_NOTEBOOK (name_to_widget (gtk_window, "notebook"));
+ GTK_NOTEBOOK (name_to_widget (s, "notebook"));
gtk_notebook_set_page (notebook, 1);
}
if (crapplet_p)
{
GtkWidget *capplet;
- GtkWidget *top_vbox;
-
- capplet = capplet_widget_new ();
+ GtkWidget *outer_vbox;
- top_vbox = GTK_BIN (gtk_window)->child;
+ gtk_widget_hide (s->toplevel_widget);
- gtk_widget_ref (top_vbox);
- gtk_container_remove (GTK_CONTAINER (gtk_window), top_vbox);
- GTK_OBJECT_SET_FLAGS (top_vbox, GTK_FLOATING);
+ capplet = capplet_widget_new ();
+ /* Make there be a "Close" button instead of "OK" and "Cancel" */
+# ifdef HAVE_CRAPPLET_IMMEDIATE
+ capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
+# endif /* HAVE_CRAPPLET_IMMEDIATE */
/* 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);
+ gtk_widget_hide (name_to_widget (s, "menubar"));
- /* Hook up the Control Center's redundant Help button, too. */
- gtk_signal_connect (GTK_OBJECT (capplet), "help",
- GTK_SIGNAL_FUNC (doc_menu_cb), 0);
+ /* Reparent our top-level container to be a child of the capplet
+ window.
+ */
+ outer_vbox = GTK_BIN (s->toplevel_widget)->child;
+ gtk_widget_ref (outer_vbox);
+ gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
+ outer_vbox);
+ STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
+ gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
+
+ /* Find the window above us, and set the title and close handler. */
+ {
+ GtkWidget *window = capplet;
+ while (window && !GTK_IS_WINDOW (window))
+ window = GET_PARENT (window);
+ if (window)
+ {
+ gtk_window_set_title (GTK_WINDOW (window), window_title);
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
+ (gpointer) s);
+ }
+ }
- /* Issue any warnings about the running xscreensaver daemon. */
- the_network_is_not_the_computer (top_vbox);
+ s->toplevel_widget = capplet;
}
- else
# endif /* HAVE_CRAPPLET */
- {
- gtk_widget_show (gtk_window);
- init_icon (GTK_WIDGET(gtk_window)->window);
- /* Issue any warnings about the running xscreensaver daemon. */
- the_network_is_not_the_computer (gtk_window);
- }
+
+ /* The Gnome folks hate the menubar. I think it's important to have access
+ to the commands on the File menu (Restart Daemon, etc.) and to the
+ About and Documentation commands on the Help menu.
+ */
+#if 0
+#ifdef HAVE_GTK2
+ gtk_widget_hide (name_to_widget (s, "menubar"));
+#endif
+#endif
+
+ free (window_title);
+ window_title = 0;
+
+#ifdef HAVE_GTK2
+ /* After picking the default size, allow -geometry to override it. */
+ if (geom)
+ gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
+#endif
+
+ gtk_widget_show (s->toplevel_widget);
+ init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
+ fix_preview_visual (s);
+
+ /* Realize page zero, so that we can diddle the scrollbar when the
+ user tabs back to it -- otherwise, the current hack isn't scrolled
+ to the first time they tab back there, when started with "-prefs".
+ (Though it is if they then tab away, and back again.)
+
+ #### Bah! This doesn't work. Gtk eats my ass! Someone who
+ #### understands this crap, explain to me how to make this work.
+ */
+ gtk_widget_realize (name_to_widget (s, "demos_table"));
+
+
+ gtk_timeout_add (60 * 1000, check_blanked_timer, s);
+
+
+ /* Handle the --settings command-line argument. */
+ if (settings_p)
+ gtk_timeout_add (500, settings_timer, 0);
+
+
+ /* Issue any warnings about the running xscreensaver daemon. */
+ if (! s->debug_p)
+ the_network_is_not_the_computer (s);
+
+
+ if (senescent_p())
+ warning_dialog (s->toplevel_widget,
+ _("Warning:\n\n"
+ "This version of xscreensaver is VERY OLD!\n"
+ "Please upgrade!\n"
+ "\n"
+ "http://www.jwz.org/xscreensaver/\n"
+ "\n"
+ "(If this is the latest version that your distro ships, then\n"
+ "your distro is doing you a disservice. Build from source.)\n"
+ ),
+ D_NONE, 7);
+
/* 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,
Xt so that we could process the command line and use the X resource
manager.
*/
- initializing_p = False;
+ s->initializing_p = False;
+
+ /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
+ after we start up. Otherwise, it always appears scrolled to the top
+ when in crapplet-mode. */
+ gtk_timeout_add (500, delayed_scroll_kludge, s);
+
+
+#if 1
+ /* Load every configurator in turn, to scan them for errors all at once. */
+ if (s->debug_p)
+ {
+ int i;
+ for (i = 0; i < p->screenhacks_count; i++)
+ {
+ screenhack *hack = p->screenhacks[i];
+ conf_data *d = load_configurator (hack->command, s->debug_p);
+ if (d) free_conf_data (d);
+ }
+ }
+#endif
+
# ifdef HAVE_CRAPPLET
if (crapplet_p)
# endif /* HAVE_CRAPPLET */
gtk_main ();
+ kill_preview_subproc (s, False);
exit (0);
}